@@ -7,6 +7,7 @@ package golang
77import (
88 "bytes"
99 "context"
10+ "encoding/json"
1011 "fmt"
1112 "go/ast"
1213 "go/constant"
@@ -48,37 +49,47 @@ import (
4849// It is formatted in one of several formats as determined by the
4950// HoverKind setting.
5051type hoverResult struct {
51- // synopsis is a single sentence synopsis of the symbol's documentation.
52+ // The fields below are exported to define the JSON hover format.
53+ // TODO(golang/go#70233): (re)remove support for JSON hover.
54+
55+ // Synopsis is a single sentence Synopsis of the symbol's documentation.
5256 //
53- // TODO(adonovan): in what syntax? It (usually) comes from doc.synopsis ,
57+ // TODO(adonovan): in what syntax? It (usually) comes from doc.Synopsis ,
5458 // which produces "Text" form, but it may be fed to
5559 // DocCommentToMarkdown, which expects doc comment syntax.
56- synopsis string
60+ Synopsis string
5761
58- // fullDocumentation is the symbol's full documentation.
59- fullDocumentation string
62+ // FullDocumentation is the symbol's full documentation.
63+ FullDocumentation string
6064
61- // signature is the symbol's signature .
62- signature string
65+ // Signature is the symbol's Signature .
66+ Signature string
6367
64- // singleLine is a single line describing the symbol.
68+ // SingleLine is a single line describing the symbol.
6569 // This is recommended only for use in clients that show a single line for hover.
66- singleLine string
70+ SingleLine string
6771
68- // symbolName is the human-readable name to use for the symbol in links.
69- symbolName string
72+ // SymbolName is the human-readable name to use for the symbol in links.
73+ SymbolName string
7074
71- // linkPath is the path of the package enclosing the given symbol,
75+ // LinkPath is the path of the package enclosing the given symbol,
7276 // with the module portion (if any) replaced by "module@version".
7377 //
7478 // For example: "github.com/google/go-github/v48@v48.1.0/github".
7579 //
76- // Use LinkTarget + "/" + linkPath + "#" + LinkAnchor to form a pkgsite URL.
77- linkPath string
80+ // Use LinkTarget + "/" + LinkPath + "#" + LinkAnchor to form a pkgsite URL.
81+ LinkPath string
7882
79- // linkAnchor is the pkg.go.dev link anchor for the given symbol.
83+ // LinkAnchor is the pkg.go.dev link anchor for the given symbol.
8084 // For example, the "Node" part of "pkg.go.dev/go/ast#Node".
81- linkAnchor string
85+ LinkAnchor string
86+
87+ // New fields go below, and are unexported. The existing
88+ // exported fields are underspecified and have already
89+ // constrained our movements too much. A detailed JSON
90+ // interface might be nice, but it needs a design and a
91+ // precise specification.
92+ // TODO(golang/go#70233): (re)deprecate the JSON hover output.
8293
8394 // typeDecl is the declaration syntax for a type,
8495 // or "" for a non-type.
@@ -284,9 +295,9 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
284295 typesinternal .SetVarKind (v , typesinternal .LocalVar )
285296 signature := types .ObjectString (v , qual )
286297 return * hoverRange , & hoverResult {
287- signature : signature ,
288- singleLine : signature ,
289- symbolName : v .Name (),
298+ Signature : signature ,
299+ SingleLine : signature ,
300+ SymbolName : v .Name (),
290301 }, nil
291302 }
292303
@@ -615,13 +626,13 @@ func hover(ctx context.Context, snapshot *cache.Snapshot, fh file.Handle, pp pro
615626 }
616627
617628 return * hoverRange , & hoverResult {
618- synopsis : doc .Synopsis (docText ),
619- fullDocumentation : docText ,
620- singleLine : singleLineSignature ,
621- symbolName : linkName ,
622- signature : signature ,
623- linkPath : linkPath ,
624- linkAnchor : anchor ,
629+ Synopsis : doc .Synopsis (docText ),
630+ FullDocumentation : docText ,
631+ SingleLine : singleLineSignature ,
632+ SymbolName : linkName ,
633+ Signature : signature ,
634+ LinkPath : linkPath ,
635+ LinkAnchor : anchor ,
625636 typeDecl : typeDecl ,
626637 methods : methods ,
627638 promotedFields : fields ,
@@ -638,8 +649,8 @@ func hoverBuiltin(ctx context.Context, snapshot *cache.Snapshot, obj types.Objec
638649 if obj .Name () == "Error" {
639650 signature := obj .String ()
640651 return & hoverResult {
641- signature : signature ,
642- singleLine : signature ,
652+ Signature : signature ,
653+ SingleLine : signature ,
643654 // TODO(rfindley): these are better than the current behavior.
644655 // SymbolName: "(error).Error",
645656 // LinkPath: "builtin",
@@ -682,13 +693,13 @@ func hoverBuiltin(ctx context.Context, snapshot *cache.Snapshot, obj types.Objec
682693
683694 docText := comment .Text ()
684695 return & hoverResult {
685- synopsis : doc .Synopsis (docText ),
686- fullDocumentation : docText ,
687- signature : signature ,
688- singleLine : obj .String (),
689- symbolName : obj .Name (),
690- linkPath : "builtin" ,
691- linkAnchor : obj .Name (),
696+ Synopsis : doc .Synopsis (docText ),
697+ FullDocumentation : docText ,
698+ Signature : signature ,
699+ SingleLine : obj .String (),
700+ SymbolName : obj .Name (),
701+ LinkPath : "builtin" ,
702+ LinkAnchor : obj .Name (),
692703 }, nil
693704}
694705
@@ -740,9 +751,9 @@ func hoverImport(ctx context.Context, snapshot *cache.Snapshot, pkg *cache.Packa
740751
741752 docText := comment .Text ()
742753 return rng , & hoverResult {
743- signature : "package " + string (impMetadata .Name ),
744- synopsis : doc .Synopsis (docText ),
745- fullDocumentation : docText ,
754+ Signature : "package " + string (impMetadata .Name ),
755+ Synopsis : doc .Synopsis (docText ),
756+ FullDocumentation : docText ,
746757 }, nil
747758}
748759
@@ -798,9 +809,9 @@ func hoverPackageName(pkg *cache.Package, pgf *parsego.File) (protocol.Range, *h
798809 }
799810
800811 return rng , & hoverResult {
801- signature : "package " + string (pkg .Metadata ().Name ),
802- synopsis : doc .Synopsis (docText ),
803- fullDocumentation : docText ,
812+ Signature : "package " + string (pkg .Metadata ().Name ),
813+ Synopsis : doc .Synopsis (docText ),
814+ FullDocumentation : docText ,
804815 footer : footer ,
805816 }, nil
806817}
@@ -926,8 +937,8 @@ func hoverLit(pgf *parsego.File, lit *ast.BasicLit, pos token.Pos) (protocol.Ran
926937 }
927938 hover := b .String ()
928939 return rng , & hoverResult {
929- synopsis : hover ,
930- fullDocumentation : hover ,
940+ Synopsis : hover ,
941+ FullDocumentation : hover ,
931942 }, nil
932943}
933944
@@ -966,7 +977,7 @@ func hoverReturnStatement(pgf *parsego.File, path []ast.Node, ret *ast.ReturnStm
966977 }
967978 buf .WriteByte (')' )
968979 return rng , & hoverResult {
969- signature : buf .String (),
980+ Signature : buf .String (),
970981 }, nil
971982}
972983
@@ -1005,9 +1016,9 @@ func hoverEmbed(fh file.Handle, rng protocol.Range, pattern string) (protocol.Ra
10051016 }
10061017
10071018 res := & hoverResult {
1008- signature : fmt .Sprintf ("Embedding %q" , pattern ),
1009- synopsis : s .String (),
1010- fullDocumentation : s .String (),
1019+ Signature : fmt .Sprintf ("Embedding %q" , pattern ),
1020+ Synopsis : s .String (),
1021+ FullDocumentation : s .String (),
10111022 }
10121023 return rng , res , nil
10131024}
@@ -1242,10 +1253,17 @@ func formatHover(h *hoverResult, options *settings.Options, pkgURL func(path Pac
12421253
12431254 switch options .HoverKind {
12441255 case settings .SingleLine :
1245- return h .singleLine , nil
1256+ return h .SingleLine , nil
12461257
12471258 case settings .NoDocumentation :
1248- return maybeFenced (h .signature ), nil
1259+ return maybeFenced (h .Signature ), nil
1260+
1261+ case settings .Structured :
1262+ b , err := json .Marshal (h )
1263+ if err != nil {
1264+ return "" , err
1265+ }
1266+ return string (b ), nil
12491267
12501268 case settings .SynopsisDocumentation , settings .FullDocumentation :
12511269 var sections [][]string // assembled below
@@ -1256,20 +1274,20 @@ func formatHover(h *hoverResult, options *settings.Options, pkgURL func(path Pac
12561274 // but not Signature, which is redundant (= TypeDecl + "\n" + Methods).
12571275 // For all other symbols, we display Signature;
12581276 // TypeDecl and Methods are empty.
1259- // (Now that JSON is no more, we could rationalize this.)
1277+ // TODO(golang/go#70233): When JSON is no more, we could rationalize this.
12601278 if h .typeDecl != "" {
12611279 sections = append (sections , []string {maybeFenced (h .typeDecl )})
12621280 } else {
1263- sections = append (sections , []string {maybeFenced (h .signature )})
1281+ sections = append (sections , []string {maybeFenced (h .Signature )})
12641282 }
12651283
12661284 // Doc section.
12671285 var doc string
12681286 switch options .HoverKind {
12691287 case settings .SynopsisDocumentation :
1270- doc = h .synopsis
1288+ doc = h .Synopsis
12711289 case settings .FullDocumentation :
1272- doc = h .fullDocumentation
1290+ doc = h .FullDocumentation
12731291 }
12741292 if options .PreferredContentFormat == protocol .Markdown {
12751293 doc = DocCommentToMarkdown (doc , options )
@@ -1392,34 +1410,34 @@ func StdSymbolOf(obj types.Object) *stdlib.Symbol {
13921410
13931411// If pkgURL is non-nil, it should be used to generate doc links.
13941412func formatLink (h * hoverResult , options * settings.Options , pkgURL func (path PackagePath , fragment string ) protocol.URI ) string {
1395- if options .LinksInHover == settings .LinksInHover_None || h .linkPath == "" {
1413+ if options .LinksInHover == settings .LinksInHover_None || h .LinkPath == "" {
13961414 return ""
13971415 }
13981416 var url protocol.URI
13991417 var caption string
14001418 if pkgURL != nil { // LinksInHover == "gopls"
14011419 // Discard optional module version portion.
14021420 // (Ideally the hoverResult would retain the structure...)
1403- path := h .linkPath
1404- if module , versionDir , ok := strings .Cut (h .linkPath , "@" ); ok {
1421+ path := h .LinkPath
1422+ if module , versionDir , ok := strings .Cut (h .LinkPath , "@" ); ok {
14051423 // "module@version/dir"
14061424 path = module
14071425 if _ , dir , ok := strings .Cut (versionDir , "/" ); ok {
14081426 path += "/" + dir
14091427 }
14101428 }
1411- url = pkgURL (PackagePath (path ), h .linkAnchor )
1429+ url = pkgURL (PackagePath (path ), h .LinkAnchor )
14121430 caption = "in gopls doc viewer"
14131431 } else {
14141432 if options .LinkTarget == "" {
14151433 return ""
14161434 }
1417- url = cache .BuildLink (options .LinkTarget , h .linkPath , h .linkAnchor )
1435+ url = cache .BuildLink (options .LinkTarget , h .LinkPath , h .LinkAnchor )
14181436 caption = "on " + options .LinkTarget
14191437 }
14201438 switch options .PreferredContentFormat {
14211439 case protocol .Markdown :
1422- return fmt .Sprintf ("[`%s` %s](%s)" , h .symbolName , caption , url )
1440+ return fmt .Sprintf ("[`%s` %s](%s)" , h .SymbolName , caption , url )
14231441 case protocol .PlainText :
14241442 return ""
14251443 default :
0 commit comments