Skip to content

Commit a7fbd1e

Browse files
committed
gopls/internal/lsp/source: show both the original declaration and the value of constants in hover
This change improves the hover information for constants by showing both the original declaration and the value. The value is displayed as an inline comment. If the original declaration and the value are the same, there will be no inline comment. Examples: ```go const duration time.Duration = 15*time.Minute + 10*time.Second // 15m10s const octal untyped int = 0o777 // 511 const expr untyped int = 2 << (0b111&0b101 - 2) // 16 const boolean untyped bool = (55 - 3) == (26 * 2) // true const dec untyped int = 500 ``` Other changes: * Calls to `objectString` that format methods or variables have been replaced with `types.ObjectString`. * The logic of inferred signature formatting has been extracted from `objectString` to a new function `inferredSignatureString`. * Remove unused function `extractFieldList`. Fixes golang/go#47453
1 parent 13850b3 commit a7fbd1e

File tree

6 files changed

+184
-47
lines changed

6 files changed

+184
-47
lines changed

gopls/internal/lsp/source/hover.go

Lines changed: 59 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Po
143143
// There's not much useful information to provide.
144144
if selectedType != nil {
145145
fakeObj := types.NewVar(obj.Pos(), obj.Pkg(), obj.Name(), selectedType)
146-
signature := objectString(fakeObj, qf, nil)
146+
signature := types.ObjectString(fakeObj, qf)
147147
return rng, &HoverJSON{
148148
Signature: signature,
149149
SingleLine: signature,
@@ -168,10 +168,15 @@ func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Po
168168
docText := comment.Text()
169169

170170
// By default, types.ObjectString provides a reasonable signature.
171-
signature := objectString(obj, qf, nil)
171+
signature := objectString(obj, qf, declPos, declPGF.Tok, spec)
172+
singleLineSignature := signature
173+
172174
// TODO(rfindley): we could do much better for inferred signatures.
173175
if inferred := inferredSignature(pkg.GetTypesInfo(), ident); inferred != nil {
174-
signature = objectString(obj, qf, inferred)
176+
s := inferredSignatureString(obj, qf, inferred)
177+
if s != "" {
178+
signature = s
179+
}
175180
}
176181

177182
// For "objects defined by a type spec", the signature produced by
@@ -214,7 +219,7 @@ func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Po
214219
if (m.Obj().Exported() || m.Obj().Pkg() == pkg.GetTypes()) && len(m.Index()) == 1 {
215220
b.WriteString(sep)
216221
sep = "\n"
217-
b.WriteString(objectString(m.Obj(), qf, nil))
222+
b.WriteString(types.ObjectString(m.Obj(), qf))
218223
}
219224
}
220225
}
@@ -321,7 +326,7 @@ func hover(ctx context.Context, snapshot Snapshot, fh FileHandle, pp protocol.Po
321326
return rng, &HoverJSON{
322327
Synopsis: doc.Synopsis(docText),
323328
FullDocumentation: docText,
324-
SingleLine: objectString(obj, qf, nil),
329+
SingleLine: singleLineSignature,
325330
SymbolName: linkName,
326331
Signature: signature,
327332
LinkPath: linkPath,
@@ -577,12 +582,10 @@ func hoverLit(pgf *ParsedGoFile, lit *ast.BasicLit, pos token.Pos) (protocol.Ran
577582
}, nil
578583
}
579584

580-
// objectString is a wrapper around the types.ObjectString function.
581-
// It handles adding more information to the object string.
582-
//
583-
// TODO(rfindley): this function does too much. We should lift the special
584-
// handling to callsites.
585-
func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string {
585+
// inferredSignatureString is a wrapper around the types.ObjectString function
586+
// that adds more information to inferred signatures. It will return an empty string
587+
// if passed types.Object is not a signature.
588+
func inferredSignatureString(obj types.Object, qf types.Qualifier, inferred *types.Signature) string {
586589
// If the signature type was inferred, prefer the inferred signature with a
587590
// comment showing the generic signature.
588591
if sig, _ := obj.Type().(*types.Signature); sig != nil && typeparams.ForSignature(sig).Len() > 0 && inferred != nil {
@@ -597,22 +600,58 @@ func objectString(obj types.Object, qf types.Qualifier, inferred *types.Signatur
597600
str += "// " + types.TypeString(sig, qf)
598601
return str
599602
}
603+
return ""
604+
}
605+
606+
// objectString is a wrapper around the types.ObjectString function.
607+
// It handles adding more information to the object string.
608+
func objectString(obj types.Object, qf types.Qualifier, declPos token.Pos, file *token.File, spec ast.Spec) string {
600609
str := types.ObjectString(obj, qf)
610+
601611
switch obj := obj.(type) {
602612
case *types.Const:
603-
str = fmt.Sprintf("%s = %s", str, obj.Val())
613+
declaration := obj.Val().String()
604614

605-
// Try to add a formatted duration as an inline comment
606-
typ, ok := obj.Type().(*types.Named)
607-
if !ok {
608-
break
615+
// Try to use the original declaration.
616+
switch obj.Val().Kind() {
617+
case constant.String:
618+
// Usually the original declaration of a string doesn't carry much information.
619+
// Also strings can be very long. So, just use the constant's value.
620+
621+
default:
622+
if file == nil || spec == nil {
623+
break
624+
}
625+
626+
switch spec := spec.(type) {
627+
case *ast.ValueSpec:
628+
for i, name := range spec.Names {
629+
if declPos == name.Pos() {
630+
if i < len(spec.Values) {
631+
declaration = FormatNodeFile(file, spec.Values[i])
632+
}
633+
break
634+
}
635+
}
636+
}
609637
}
610-
pkg := typ.Obj().Pkg()
611-
if pkg.Path() == "time" && typ.Obj().Name() == "Duration" {
612-
if d, ok := constant.Int64Val(obj.Val()); ok {
613-
str += " // " + time.Duration(d).String()
638+
639+
comment := obj.Val().String()
640+
switch typ := obj.Type().(type) {
641+
case *types.Named:
642+
// Try to add a formatted duration as an inline comment.
643+
pkg := typ.Obj().Pkg()
644+
if pkg.Path() == "time" && typ.Obj().Name() == "Duration" {
645+
if d, ok := constant.Int64Val(obj.Val()); ok {
646+
comment = time.Duration(d).String()
647+
}
614648
}
615649
}
650+
651+
str += " = " + declaration
652+
if declaration != comment {
653+
str += " // " + comment
654+
}
616655
}
617656
return str
618657
}
@@ -708,28 +747,6 @@ func parseFull(ctx context.Context, snapshot Snapshot, fset *token.FileSet, pos
708747
return pgf, fullPos, nil
709748
}
710749

711-
// extractFieldList recursively tries to extract a field list.
712-
// If it is not found, nil is returned.
713-
func extractFieldList(specType ast.Expr) *ast.FieldList {
714-
switch t := specType.(type) {
715-
case *ast.StructType:
716-
return t.Fields
717-
case *ast.InterfaceType:
718-
return t.Methods
719-
case *ast.ArrayType:
720-
return extractFieldList(t.Elt)
721-
case *ast.MapType:
722-
// Map value has a greater chance to be a struct
723-
if fields := extractFieldList(t.Value); fields != nil {
724-
return fields
725-
}
726-
return extractFieldList(t.Key)
727-
case *ast.ChanType:
728-
return extractFieldList(t.Value)
729-
}
730-
return nil
731-
}
732-
733750
func formatHover(h *HoverJSON, options *Options) (string, error) {
734751
signature := formatSignature(h, options)
735752

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,66 @@
11
package a
22

3-
import "time"
3+
import (
4+
"math"
5+
"time"
6+
)
47

58
// dur is a constant of type time.Duration.
69
const dur = 15*time.Minute + 10*time.Second + 350*time.Millisecond //@dur,hoverdef("dur", dur)
10+
11+
// Numbers.
12+
func _() {
13+
const hex, bin = 0xe34e, 0b1001001
14+
15+
const (
16+
// no inline comment
17+
decimal = 153
18+
19+
numberWithUnderscore int64 = 10_000_000_000
20+
octal = 0o777
21+
expr = 2 << (0b111&0b101 - 2)
22+
boolean = (55 - 3) == (26 * 2)
23+
)
24+
25+
_ = decimal //@mark(decimalConst, "decimal"),hoverdef("decimal", decimalConst)
26+
_ = hex //@mark(hexConst, "hex"),hoverdef("hex", hexConst)
27+
_ = bin //@mark(binConst, "bin"),hoverdef("bin", binConst)
28+
_ = numberWithUnderscore //@mark(numberWithUnderscoreConst, "numberWithUnderscore"),hoverdef("numberWithUnderscore", numberWithUnderscoreConst)
29+
_ = octal //@mark(octalConst, "octal"),hoverdef("octal", octalConst)
30+
_ = expr //@mark(exprConst, "expr"),hoverdef("expr", exprConst)
31+
_ = boolean //@mark(boolConst, "boolean"),hoverdef("boolean", boolConst)
32+
33+
const ln10 = 2.30258509299404568401799145468436420760110148862877297603332790
34+
35+
_ = ln10 //@mark(ln10Const, "ln10"),hoverdef("ln10", ln10Const)
36+
}
37+
38+
// Iota.
39+
func _() {
40+
const (
41+
a = 1 << iota
42+
b
43+
)
44+
45+
_ = a //@mark(aIota, "a"),hoverdef("a", aIota)
46+
_ = b //@mark(bIota, "b"),hoverdef("b", bIota)
47+
}
48+
49+
// Strings.
50+
func _() {
51+
const (
52+
str = "hello" + " " + "world"
53+
longStr = `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur eget ipsum non nunc
54+
molestie mattis id quis augue. Mauris dictum tincidunt ipsum, in auctor arcu congue eu.
55+
Morbi hendrerit fringilla libero commodo varius. Vestibulum in enim rutrum, rutrum tellus
56+
aliquet, luctus enim. Nunc sem ex, consectetur id porta nec, placerat vel urna.`
57+
)
58+
59+
_ = str //@mark(strConst, "str"),hoverdef("str", strConst)
60+
_ = longStr //@mark(longStrConst, "longStr"),hoverdef("longStr", longStrConst)
61+
}
62+
63+
// Constants from other packages.
64+
func _() {
65+
_ = math.MaxFloat32 //@mark(maxFloat32Const, "MaxFloat32"),hoverdef("MaxFloat32", maxFloat32Const)
66+
}
Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,67 @@
11
-- dur-hoverdef --
22
```go
3-
const dur time.Duration = 910350000000 // 15m10.35s
3+
const dur time.Duration = 15*time.Minute + 10*time.Second + 350*time.Millisecond // 15m10.35s
44
```
55

66
dur is a constant of type time.Duration.
77

8+
-- decimalConst-hoverdef --
9+
```go
10+
const decimal untyped int = 153
11+
```
12+
13+
no inline comment
14+
15+
-- hexConst-hoverdef --
16+
```go
17+
const hex untyped int = 0xe34e // 58190
18+
```
19+
-- binConst-hoverdef --
20+
```go
21+
const bin untyped int = 0b1001001 // 73
22+
```
23+
-- numberWithUnderscoreConst-hoverdef --
24+
```go
25+
const numberWithUnderscore int64 = 10_000_000_000 // 10000000000
26+
```
27+
-- octalConst-hoverdef --
28+
```go
29+
const octal untyped int = 0o777 // 511
30+
```
31+
-- exprConst-hoverdef --
32+
```go
33+
const expr untyped int = 2 << (0b111&0b101 - 2) // 16
34+
```
35+
-- boolConst-hoverdef --
36+
```go
37+
const boolean untyped bool = (55 - 3) == (26 * 2) // true
38+
```
39+
-- ln10Const-hoverdef --
40+
```go
41+
const ln10 untyped float = 2.30258509299404568401799145468436420760110148862877297603332790 // 2.30259
42+
```
43+
-- aIota-hoverdef --
44+
```go
45+
const a untyped int = 1 << iota // 1
46+
```
47+
-- bIota-hoverdef --
48+
```go
49+
const b untyped int = 2
50+
```
51+
-- strConst-hoverdef --
52+
```go
53+
const str untyped string = "hello world"
54+
```
55+
-- longStrConst-hoverdef --
56+
```go
57+
const longStr untyped string = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur e...
58+
```
59+
-- maxFloat32Const-hoverdef --
60+
```go
61+
const math.MaxFloat32 untyped float = 0x1p127 * (1 + (1 - 0x1p-23)) // 3.40282e+38
62+
```
63+
64+
Floating-point limit values.
65+
66+
67+
[`math.MaxFloat32` on pkg.go.dev](https://pkg.go.dev/math#MaxFloat32)

gopls/internal/lsp/testdata/summary.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ SemanticTokenCount = 3
1616
SuggestedFixCount = 65
1717
FunctionExtractionCount = 27
1818
MethodExtractionCount = 6
19-
DefinitionsCount = 47
19+
DefinitionsCount = 60
2020
TypeDefinitionsCount = 18
2121
HighlightsCount = 69
2222
InlayHintsCount = 4

gopls/internal/lsp/testdata/summary_go1.18.txt.golden

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ SemanticTokenCount = 3
1616
SuggestedFixCount = 71
1717
FunctionExtractionCount = 27
1818
MethodExtractionCount = 6
19-
DefinitionsCount = 47
19+
DefinitionsCount = 60
2020
TypeDefinitionsCount = 18
2121
HighlightsCount = 69
2222
InlayHintsCount = 5

gopls/internal/regtest/marker/testdata/hover/hover.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func _() {
1515
}
1616
-- @abc/hover.md --
1717
```go
18-
const abc untyped int = 42
18+
const abc untyped int = 0x2a // 42
1919
```
2020

2121
@hover("b", "abc", abc),hover(" =", "abc", abc)

0 commit comments

Comments
 (0)