Skip to content

Commit 43a8bd9

Browse files
findleyrgopherbot
authored andcommitted
[gopls-release-branch.0.18] gopls: temporarily reinstate the "Structured" hover kind
As described in golang/go#71879, the removal of the experimental "Structured" hover kind unexpectedly broke vim-go. Reinstate support for this setting, with tests, so that we can proceed with its deprecation more cautiously. For golang/go#71879 Change-Id: I6d22852aa10126c84b66f4345fbbdcf4cefbd182 Reviewed-on: https://go-review.googlesource.com/c/tools/+/651238 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Alan Donovan <[email protected]> Auto-Submit: Robert Findley <[email protected]> (cherry picked from commit f2beb33) Reviewed-on: https://go-review.googlesource.com/c/tools/+/651239
1 parent c075684 commit 43a8bd9

File tree

6 files changed

+135
-71
lines changed

6 files changed

+135
-71
lines changed

gopls/doc/settings.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,9 @@ Must be one of:
428428
* `"FullDocumentation"`
429429
* `"NoDocumentation"`
430430
* `"SingleLine"`
431+
* `"Structured"` is a misguided experimental setting that returns a JSON
432+
hover format. This setting should not be used, as it will be removed in a
433+
future release of gopls.
431434
* `"SynopsisDocumentation"`
432435

433436
Default: `"FullDocumentation"`.

gopls/internal/doc/api.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@
134134
"Value": "\"SingleLine\"",
135135
"Doc": ""
136136
},
137+
{
138+
"Value": "\"Structured\"",
139+
"Doc": "`\"Structured\"` is a misguided experimental setting that returns a JSON\nhover format. This setting should not be used, as it will be removed in a\nfuture release of gopls.\n"
140+
},
137141
{
138142
"Value": "\"SynopsisDocumentation\"",
139143
"Doc": ""

gopls/internal/golang/hover.go

Lines changed: 77 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ package golang
77
import (
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.
5051
type 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/[email protected]/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.
13941412
func 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:

gopls/internal/settings/settings.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -798,6 +798,11 @@ const (
798798
NoDocumentation HoverKind = "NoDocumentation"
799799
SynopsisDocumentation HoverKind = "SynopsisDocumentation"
800800
FullDocumentation HoverKind = "FullDocumentation"
801+
802+
// Structured is a misguided experimental setting that returns a JSON
803+
// hover format. This setting should not be used, as it will be removed in a
804+
// future release of gopls.
805+
Structured HoverKind = "Structured"
801806
)
802807

803808
type VulncheckMode string
@@ -1073,14 +1078,15 @@ func (o *Options) setOne(name string, value any) (applied []CounterPath, _ error
10731078
AllSymbolScope)
10741079

10751080
case "hoverKind":
1076-
if s, ok := value.(string); ok && strings.EqualFold(s, "structured") {
1077-
return nil, deprecatedError("the experimental hoverKind='structured' setting was removed in gopls/v0.18.0 (https://go.dev/issue/70233)")
1078-
}
1081+
// TODO(rfindley): reinstate the deprecation of Structured hover by making
1082+
// it a warning in gopls v0.N+1, and removing it in gopls v0.N+2.
10791083
return setEnum(&o.HoverKind, value,
10801084
NoDocumentation,
10811085
SingleLine,
10821086
SynopsisDocumentation,
1083-
FullDocumentation)
1087+
FullDocumentation,
1088+
Structured,
1089+
)
10841090

10851091
case "linkTarget":
10861092
return nil, setString(&o.LinkTarget, value)

gopls/internal/settings/settings_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -91,19 +91,19 @@ func TestOptions_Set(t *testing.T) {
9191
},
9292
},
9393
{
94-
name: "hoverKind",
95-
value: "Structured",
96-
wantError: true,
94+
name: "hoverKind",
95+
value: "Structured",
96+
// wantError: true, // TODO(rfindley): reinstate this error
9797
check: func(o Options) bool {
98-
return o.HoverKind == FullDocumentation
98+
return o.HoverKind == Structured
9999
},
100100
},
101101
{
102-
name: "ui.documentation.hoverKind",
103-
value: "Structured",
104-
wantError: true,
102+
name: "ui.documentation.hoverKind",
103+
value: "Structured",
104+
// wantError: true, // TODO(rfindley): reinstate this error
105105
check: func(o Options) bool {
106-
return o.HoverKind == FullDocumentation
106+
return o.HoverKind == Structured
107107
},
108108
},
109109
{
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
This test demonstrates support for "hoverKind": "Structured".
2+
3+
Its size expectations assume a 64-bit machine.
4+
5+
-- flags --
6+
-skip_goarch=386,arm
7+
8+
-- go.mod --
9+
module example.com/p
10+
11+
go 1.18
12+
13+
-- settings.json --
14+
{
15+
"hoverKind": "Structured"
16+
}
17+
-- p.go --
18+
package p
19+
20+
// MyType is a type.
21+
type MyType struct { //@ hover("MyType", "MyType", MyType)
22+
F int // a field
23+
S string // a string field
24+
}
25+
26+
// MyFunc is a function.
27+
func MyFunc(i int) string { //@ hover("MyFunc", "MyFunc", MyFunc)
28+
return ""
29+
}
30+
-- @MyFunc --
31+
{"Synopsis":"MyFunc is a function.","FullDocumentation":"MyFunc is a function.\n","Signature":"func MyFunc(i int) string","SingleLine":"func MyFunc(i int) string","SymbolName":"p.MyFunc","LinkPath":"example.com/p","LinkAnchor":"MyFunc"}
32+
-- @MyType --
33+
{"Synopsis":"MyType is a type.","FullDocumentation":"MyType is a type.\n","Signature":"type MyType struct { // size=24 (0x18)\n\tF int // a field\n\tS string // a string field\n}\n","SingleLine":"type MyType struct{F int; S string}","SymbolName":"p.MyType","LinkPath":"example.com/p","LinkAnchor":"MyType"}

0 commit comments

Comments
 (0)