@@ -15,6 +15,7 @@ import (
15
15
"go/format"
16
16
"go/token"
17
17
"go/types"
18
+ "go/version"
18
19
"io/fs"
19
20
"path/filepath"
20
21
"sort"
@@ -80,10 +81,6 @@ type hoverJSON struct {
80
81
// For example, the "Node" part of "pkg.go.dev/go/ast#Node".
81
82
LinkAnchor string `json:"linkAnchor"`
82
83
83
- // stdVersion is the Go release version at which this symbol became available.
84
- // It is nil for non-std library.
85
- stdVersion * stdlib.Version
86
-
87
84
// New fields go below, and are unexported. The existing
88
85
// exported fields are underspecified and have already
89
86
// constrained our movements too much. A detailed JSON
@@ -103,6 +100,10 @@ type hoverJSON struct {
103
100
// fields of a (struct) type that were promoted through an
104
101
// embedded field.
105
102
promotedFields string
103
+
104
+ // footer is additional content to insert at the bottom of the hover
105
+ // documentation, before the pkgdoc link.
106
+ footer string
106
107
}
107
108
108
109
// Hover implements the "textDocument/hover" RPC for Go files.
@@ -602,9 +603,9 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
602
603
linkPath = strings .Replace (linkPath , mod .Path , mod .Path + "@" + mod .Version , 1 )
603
604
}
604
605
605
- var version * stdlib. Version
606
- if symbol := StdSymbolOf (obj ); symbol != nil {
607
- version = & symbol . Version
606
+ var footer string
607
+ if sym := StdSymbolOf (obj ); sym != nil && sym . Version > 0 {
608
+ footer = fmt . Sprintf ( "Added in %v" , sym . Version )
608
609
}
609
610
610
611
return * hoverRange , & hoverJSON {
@@ -618,7 +619,7 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
618
619
typeDecl : typeDecl ,
619
620
methods : methods ,
620
621
promotedFields : fields ,
621
- stdVersion : version ,
622
+ footer : footer ,
622
623
}, nil
623
624
}
624
625
@@ -733,6 +734,7 @@ func hoverImport(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Packa
733
734
734
735
docText := comment .Text ()
735
736
return rng , & hoverJSON {
737
+ Signature : "package " + string (impMetadata .Name ),
736
738
Synopsis : doc .Synopsis (docText ),
737
739
FullDocumentation : docText ,
738
740
}, nil
@@ -753,11 +755,47 @@ func hoverPackageName(pkg *cache.Package, pgf *parsego.File) (protocol.Range, *h
753
755
return protocol.Range {}, nil , err
754
756
}
755
757
docText := comment .Text ()
758
+
759
+ // List some package attributes at the bottom of the documentation, if
760
+ // applicable.
761
+ type attr struct { title , value string }
762
+ var attrs []attr
763
+
764
+ if ! metadata .IsCommandLineArguments (pkg .Metadata ().ID ) {
765
+ attrs = append (attrs , attr {"Package path" , string (pkg .Metadata ().PkgPath )})
766
+ }
767
+
768
+ if pkg .Metadata ().Module != nil {
769
+ attrs = append (attrs , attr {"Module" , pkg .Metadata ().Module .Path })
770
+ }
771
+
772
+ // Show the effective language version for this package.
773
+ if v := pkg .TypesInfo ().FileVersions [pgf .File ]; v != "" {
774
+ attr := attr {value : version .Lang (v )}
775
+ if v == pkg .Types ().GoVersion () {
776
+ attr .title = "Language version"
777
+ } else {
778
+ attr .title = "Language version (current file)"
779
+ }
780
+ attrs = append (attrs , attr )
781
+ }
782
+
783
+ // TODO(rfindley): consider exec'ing go here to compute DefaultGODEBUG, or
784
+ // propose adding GODEBUG info to go/packages.
785
+
786
+ var footer string
787
+ for i , attr := range attrs {
788
+ if i > 0 {
789
+ footer += "\n "
790
+ }
791
+ footer += fmt .Sprintf (" - %s: %s" , attr .title , attr .value )
792
+ }
793
+
756
794
return rng , & hoverJSON {
795
+ Signature : "package " + string (pkg .Metadata ().Name ),
757
796
Synopsis : doc .Synopsis (docText ),
758
797
FullDocumentation : docText ,
759
- // Note: including a signature is redundant, since the cursor is already on the
760
- // package name.
798
+ footer : footer ,
761
799
}, nil
762
800
}
763
801
@@ -1149,8 +1187,9 @@ func parseFull(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSe
1149
1187
1150
1188
// If pkgURL is non-nil, it should be used to generate doc links.
1151
1189
func formatHover (h * hoverJSON , options * settings.Options , pkgURL func (path PackagePath , fragment string ) protocol.URI ) (string , error ) {
1152
- maybeMarkdown := func (s string ) string {
1153
- if s != "" && options .PreferredContentFormat == protocol .Markdown {
1190
+ markdown := options .PreferredContentFormat == protocol .Markdown
1191
+ maybeFenced := func (s string ) string {
1192
+ if s != "" && markdown {
1154
1193
s = fmt .Sprintf ("```go\n %s\n ```" , strings .Trim (s , "\n " ))
1155
1194
}
1156
1195
return s
@@ -1161,7 +1200,7 @@ func formatHover(h *hoverJSON, options *settings.Options, pkgURL func(path Packa
1161
1200
return h .SingleLine , nil
1162
1201
1163
1202
case settings .NoDocumentation :
1164
- return maybeMarkdown (h .Signature ), nil
1203
+ return maybeFenced (h .Signature ), nil
1165
1204
1166
1205
case settings .Structured :
1167
1206
b , err := json .Marshal (h )
@@ -1170,42 +1209,70 @@ func formatHover(h *hoverJSON, options *settings.Options, pkgURL func(path Packa
1170
1209
}
1171
1210
return string (b ), nil
1172
1211
1173
- case settings .SynopsisDocumentation ,
1174
- settings .FullDocumentation :
1212
+ case settings .SynopsisDocumentation , settings .FullDocumentation :
1213
+ var sections [][]string // assembled below
1214
+
1215
+ // Signature section.
1216
+ //
1175
1217
// For types, we display TypeDecl and Methods,
1176
1218
// but not Signature, which is redundant (= TypeDecl + "\n" + Methods).
1177
1219
// For all other symbols, we display Signature;
1178
1220
// TypeDecl and Methods are empty.
1179
1221
// (This awkwardness is to preserve JSON compatibility.)
1180
- parts := []string {
1181
- maybeMarkdown (h .Signature ),
1182
- maybeMarkdown (h .typeDecl ),
1183
- formatDoc (h , options ),
1184
- maybeMarkdown (h .promotedFields ),
1185
- maybeMarkdown (h .methods ),
1186
- fmt .Sprintf ("Added in %v" , h .stdVersion ),
1187
- formatLink (h , options , pkgURL ),
1188
- }
1189
1222
if h .typeDecl != "" {
1190
- parts [0 ] = "" // type: suppress redundant Signature
1223
+ sections = append (sections , []string {maybeFenced (h .typeDecl )})
1224
+ } else {
1225
+ sections = append (sections , []string {maybeFenced (h .Signature )})
1226
+ }
1227
+
1228
+ // Doc section.
1229
+ var doc string
1230
+ switch options .HoverKind {
1231
+ case settings .SynopsisDocumentation :
1232
+ doc = h .Synopsis
1233
+ case settings .FullDocumentation :
1234
+ doc = h .FullDocumentation
1191
1235
}
1192
- if h . stdVersion == nil || * h . stdVersion == stdlib . Version ( 0 ) {
1193
- parts [ 5 ] = "" // suppress stdlib version if not applicable or initial version 1.0
1236
+ if options . PreferredContentFormat == protocol . Markdown {
1237
+ doc = DocCommentToMarkdown ( doc , options )
1194
1238
}
1239
+ sections = append (sections , []string {
1240
+ doc ,
1241
+ maybeFenced (h .promotedFields ),
1242
+ maybeFenced (h .methods ),
1243
+ })
1244
+
1245
+ // Footer section.
1246
+ sections = append (sections , []string {
1247
+ h .footer ,
1248
+ formatLink (h , options , pkgURL ),
1249
+ })
1195
1250
1196
1251
var b strings.Builder
1197
- for _ , part := range parts {
1198
- if part == "" {
1199
- continue
1252
+ newline := func () {
1253
+ if options .PreferredContentFormat == protocol .Markdown {
1254
+ b .WriteString ("\n \n " )
1255
+ } else {
1256
+ b .WriteByte ('\n' )
1200
1257
}
1201
- if b .Len () > 0 {
1202
- if options .PreferredContentFormat == protocol .Markdown {
1203
- b .WriteString ("\n \n " )
1204
- } else {
1205
- b .WriteByte ('\n' )
1258
+ }
1259
+ for _ , section := range sections {
1260
+ start := b .Len ()
1261
+ for _ , part := range section {
1262
+ if part == "" {
1263
+ continue
1264
+ }
1265
+ // When markdown is a available, insert an hline before the start of
1266
+ // the section, if there is content above.
1267
+ if markdown && b .Len () == start && start > 0 {
1268
+ newline ()
1269
+ b .WriteString ("---" )
1206
1270
}
1271
+ if b .Len () > 0 {
1272
+ newline ()
1273
+ }
1274
+ b .WriteString (part )
1207
1275
}
1208
- b .WriteString (part )
1209
1276
}
1210
1277
return b .String (), nil
1211
1278
@@ -1313,20 +1380,6 @@ func formatLink(h *hoverJSON, options *settings.Options, pkgURL func(path Packag
1313
1380
}
1314
1381
}
1315
1382
1316
- func formatDoc (h * hoverJSON , options * settings.Options ) string {
1317
- var doc string
1318
- switch options .HoverKind {
1319
- case settings .SynopsisDocumentation :
1320
- doc = h .Synopsis
1321
- case settings .FullDocumentation :
1322
- doc = h .FullDocumentation
1323
- }
1324
- if options .PreferredContentFormat == protocol .Markdown {
1325
- return DocCommentToMarkdown (doc , options )
1326
- }
1327
- return doc
1328
- }
1329
-
1330
1383
// findDeclInfo returns the syntax nodes involved in the declaration of the
1331
1384
// types.Object with position pos, searching the given list of file syntax
1332
1385
// trees.
0 commit comments