@@ -15,6 +15,7 @@ import (
1515 "go/format"
1616 "go/token"
1717 "go/types"
18+ "go/version"
1819 "io/fs"
1920 "path/filepath"
2021 "sort"
@@ -80,10 +81,6 @@ type hoverJSON struct {
8081 // For example, the "Node" part of "pkg.go.dev/go/ast#Node".
8182 LinkAnchor string `json:"linkAnchor"`
8283
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-
8784 // New fields go below, and are unexported. The existing
8885 // exported fields are underspecified and have already
8986 // constrained our movements too much. A detailed JSON
@@ -103,6 +100,10 @@ type hoverJSON struct {
103100 // fields of a (struct) type that were promoted through an
104101 // embedded field.
105102 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
106107}
107108
108109// 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
602603 linkPath = strings .Replace (linkPath , mod .Path , mod .Path + "@" + mod .Version , 1 )
603604 }
604605
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 )
608609 }
609610
610611 return * hoverRange , & hoverJSON {
@@ -618,7 +619,7 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
618619 typeDecl : typeDecl ,
619620 methods : methods ,
620621 promotedFields : fields ,
621- stdVersion : version ,
622+ footer : footer ,
622623 }, nil
623624}
624625
@@ -733,6 +734,7 @@ func hoverImport(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Packa
733734
734735 docText := comment .Text ()
735736 return rng , & hoverJSON {
737+ Signature : "package " + string (impMetadata .Name ),
736738 Synopsis : doc .Synopsis (docText ),
737739 FullDocumentation : docText ,
738740 }, nil
@@ -753,11 +755,47 @@ func hoverPackageName(pkg *cache.Package, pgf *parsego.File) (protocol.Range, *h
753755 return protocol.Range {}, nil , err
754756 }
755757 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+
756794 return rng , & hoverJSON {
795+ Signature : "package " + string (pkg .Metadata ().Name ),
757796 Synopsis : doc .Synopsis (docText ),
758797 FullDocumentation : docText ,
759- // Note: including a signature is redundant, since the cursor is already on the
760- // package name.
798+ footer : footer ,
761799 }, nil
762800}
763801
@@ -1149,8 +1187,9 @@ func parseFull(ctx context.Context, snapshot *cache.Snapshot, fset *token.FileSe
11491187
11501188// If pkgURL is non-nil, it should be used to generate doc links.
11511189func 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 {
11541193 s = fmt .Sprintf ("```go\n %s\n ```" , strings .Trim (s , "\n " ))
11551194 }
11561195 return s
@@ -1161,7 +1200,7 @@ func formatHover(h *hoverJSON, options *settings.Options, pkgURL func(path Packa
11611200 return h .SingleLine , nil
11621201
11631202 case settings .NoDocumentation :
1164- return maybeMarkdown (h .Signature ), nil
1203+ return maybeFenced (h .Signature ), nil
11651204
11661205 case settings .Structured :
11671206 b , err := json .Marshal (h )
@@ -1170,42 +1209,70 @@ func formatHover(h *hoverJSON, options *settings.Options, pkgURL func(path Packa
11701209 }
11711210 return string (b ), nil
11721211
1173- case settings .SynopsisDocumentation ,
1174- settings .FullDocumentation :
1212+ case settings .SynopsisDocumentation , settings .FullDocumentation :
1213+ var sections [][]string // assembled below
1214+
1215+ // Signature section.
1216+ //
11751217 // For types, we display TypeDecl and Methods,
11761218 // but not Signature, which is redundant (= TypeDecl + "\n" + Methods).
11771219 // For all other symbols, we display Signature;
11781220 // TypeDecl and Methods are empty.
11791221 // (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- }
11891222 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
11911235 }
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 )
11941238 }
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+ })
11951250
11961251 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' )
12001257 }
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 ("---" )
12061270 }
1271+ if b .Len () > 0 {
1272+ newline ()
1273+ }
1274+ b .WriteString (part )
12071275 }
1208- b .WriteString (part )
12091276 }
12101277 return b .String (), nil
12111278
@@ -1313,20 +1380,6 @@ func formatLink(h *hoverJSON, options *settings.Options, pkgURL func(path Packag
13131380 }
13141381}
13151382
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-
13301383// findDeclInfo returns the syntax nodes involved in the declaration of the
13311384// types.Object with position pos, searching the given list of file syntax
13321385// trees.
0 commit comments