Skip to content

Commit 792e0c7

Browse files
authored
Multiple improvements to Quick Info (#1184)
1 parent 2668cdf commit 792e0c7

File tree

4 files changed

+91
-43
lines changed

4 files changed

+91
-43
lines changed

internal/checker/exports.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,7 @@ func (c *Checker) TypePredicateToString(t *TypePredicate) string {
129129
func (c *Checker) GetExpandedParameters(signature *Signature, skipUnionExpanding bool) [][]*ast.Symbol {
130130
return c.getExpandedParameters(signature, skipUnionExpanding)
131131
}
132+
133+
func (c *Checker) GetResolvedSignature(node *ast.Node) *Signature {
134+
return c.getResolvedSignature(node, nil, CheckModeNormal)
135+
}

internal/checker/nodebuilderimpl.go

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1572,8 +1572,7 @@ type SignatureToSignatureDeclarationOptions struct {
15721572
}
15731573

15741574
func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signature, kind ast.Kind, options *SignatureToSignatureDeclarationOptions) *ast.Node {
1575-
var typeParameters *[]*ast.Node
1576-
var typeArguments *[]*ast.Node
1575+
var typeParameters []*ast.Node
15771576

15781577
expandedParams := b.ch.getExpandedParameters(signature, true /*skipUnionExpanding*/)[0]
15791578
cleanup := b.enterNewScope(signature.declaration, expandedParams, signature.typeParameters, signature.parameters, signature.mapper)
@@ -1582,21 +1581,11 @@ func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signa
15821581

15831582
if b.ctx.flags&nodebuilder.FlagsWriteTypeArgumentsOfSignature != 0 && signature.target != nil && signature.mapper != nil && signature.target.typeParameters != nil {
15841583
for _, parameter := range signature.target.typeParameters {
1585-
node := b.typeToTypeNode(b.ch.instantiateType(parameter, signature.mapper))
1586-
if typeArguments == nil {
1587-
typeArguments = &[]*ast.Node{}
1588-
}
1589-
args := append(*typeArguments, node)
1590-
typeArguments = &args
1584+
typeParameters = append(typeParameters, b.typeToTypeNode(b.ch.instantiateType(parameter, signature.mapper)))
15911585
}
15921586
} else if signature.typeParameters != nil {
15931587
for _, parameter := range signature.typeParameters {
1594-
node := b.typeParameterToDeclaration(parameter)
1595-
if typeParameters == nil {
1596-
typeParameters = &[]*ast.Node{}
1597-
}
1598-
args := append(*typeParameters, node)
1599-
typeParameters = &args
1588+
typeParameters = append(typeParameters, b.typeParameterToDeclaration(parameter))
16001589
}
16011590
}
16021591

@@ -1632,8 +1621,8 @@ func (b *nodeBuilderImpl) signatureToSignatureDeclarationHelper(signature *Signa
16321621

16331622
paramList := b.f.NewNodeList(parameters)
16341623
var typeParamList *ast.NodeList
1635-
if typeParameters != nil {
1636-
typeParamList = b.f.NewNodeList(*typeParameters)
1624+
if len(typeParameters) != 0 {
1625+
typeParamList = b.f.NewNodeList(typeParameters)
16371626
}
16381627
var modifierList *ast.ModifierList
16391628
if modifiers != nil && len(modifiers) > 0 {

internal/ls/hover.go

Lines changed: 75 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package ls
33
import (
44
"context"
55
"fmt"
6+
"slices"
67
"strings"
78

89
"github.com/microsoft/typescript-go/internal/ast"
@@ -26,7 +27,7 @@ func (l *LanguageService) ProvideHover(ctx context.Context, documentURI lsproto.
2627
}
2728
c, done := program.GetTypeCheckerForFile(ctx, file)
2829
defer done()
29-
quickInfo, declaration := getQuickInfoAndDeclarationAtLocation(c, node)
30+
quickInfo, declaration := getQuickInfoAndDeclarationAtLocation(c, getNodeForQuickInfo(node))
3031
if quickInfo != "" {
3132
return &lsproto.Hover{
3233
Contents: lsproto.MarkupContentOrMarkedStringOrMarkedStrings{
@@ -63,6 +64,7 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, node *ast.Node) (s
6364
// If the symbol has a type meaning and we're in a type context, remove value-only meanings
6465
flags &^= ast.SymbolFlagsVariable | ast.SymbolFlagsFunction
6566
}
67+
container := getContainerNode(node)
6668
var b strings.Builder
6769
if isAlias {
6870
b.WriteString("(alias) ")
@@ -93,66 +95,92 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, node *ast.Node) (s
9395
}
9496
}
9597
}
96-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
98+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
9799
b.WriteString(": ")
98-
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), nil, typeFormatFlags))
100+
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
99101
case flags&ast.SymbolFlagsEnumMember != 0:
100102
b.WriteString("(enum member) ")
101103
t := c.GetTypeOfSymbol(symbol)
102-
b.WriteString(c.TypeToStringEx(t, nil, typeFormatFlags))
104+
b.WriteString(c.TypeToStringEx(t, container, typeFormatFlags))
103105
if t.Flags()&checker.TypeFlagsLiteral != 0 {
104106
b.WriteString(" = ")
105107
b.WriteString(t.AsLiteralType().String())
106108
}
107109
case flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0:
108-
signatures := c.GetSignaturesOfType(c.GetTypeOfSymbol(symbol), checker.SignatureKindCall)
110+
signatures := getSignaturesAtLocation(c, symbol, checker.SignatureKindCall, node)
111+
if len(signatures) == 1 && signatures[0].Declaration() != nil {
112+
declaration = signatures[0].Declaration()
113+
}
109114
prefix := core.IfElse(symbol.Flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
110-
writeSignatures(&b, c, signatures, prefix, symbol)
115+
writeSignatures(&b, c, signatures, container, prefix, symbol)
111116
case flags&ast.SymbolFlagsConstructor != 0:
112-
signatures := c.GetSignaturesOfType(c.GetTypeOfSymbol(symbol.Parent), checker.SignatureKindConstruct)
113-
writeSignatures(&b, c, signatures, "constructor ", symbol.Parent)
117+
signatures := getSignaturesAtLocation(c, symbol.Parent, checker.SignatureKindConstruct, node)
118+
if len(signatures) == 1 && signatures[0].Declaration() != nil {
119+
declaration = signatures[0].Declaration()
120+
}
121+
writeSignatures(&b, c, signatures, container, "constructor ", symbol.Parent)
114122
case flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
115-
b.WriteString(core.IfElse(symbol.Flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
116-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
117-
params := c.GetDeclaredTypeOfSymbol(symbol).AsInterfaceType().LocalTypeParameters()
118-
writeTypeParams(&b, c, params)
123+
if node.Kind == ast.KindThisKeyword || ast.IsThisInTypeQuery(node) {
124+
b.WriteString("this")
125+
} else {
126+
b.WriteString(core.IfElse(symbol.Flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
127+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
128+
params := c.GetDeclaredTypeOfSymbol(symbol).AsInterfaceType().LocalTypeParameters()
129+
writeTypeParams(&b, c, params)
130+
}
119131
if flags&ast.SymbolFlagsInterface != 0 {
120132
declaration = core.Find(symbol.Declarations, ast.IsInterfaceDeclaration)
121133
}
122134
case flags&ast.SymbolFlagsEnum != 0:
123135
b.WriteString("enum ")
124-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
136+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
125137
case flags&ast.SymbolFlagsModule != 0:
126138
b.WriteString(core.IfElse(symbol.ValueDeclaration != nil && ast.IsSourceFile(symbol.ValueDeclaration), "module ", "namespace "))
127-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
139+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
128140
case flags&ast.SymbolFlagsTypeParameter != 0:
129141
b.WriteString("(type parameter) ")
130142
tp := c.GetDeclaredTypeOfSymbol(symbol)
131-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
143+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
132144
cons := c.GetConstraintOfTypeParameter(tp)
133145
if cons != nil {
134146
b.WriteString(" extends ")
135-
b.WriteString(c.TypeToStringEx(cons, nil, typeFormatFlags))
147+
b.WriteString(c.TypeToStringEx(cons, container, typeFormatFlags))
136148
}
137149
declaration = core.Find(symbol.Declarations, ast.IsTypeParameterDeclaration)
138150
case flags&ast.SymbolFlagsTypeAlias != 0:
139151
b.WriteString("type ")
140-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
152+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
141153
writeTypeParams(&b, c, c.GetTypeAliasTypeParameters(symbol))
142154
if len(symbol.Declarations) != 0 {
143155
b.WriteString(" = ")
144-
b.WriteString(c.TypeToStringEx(c.GetDeclaredTypeOfSymbol(symbol), nil, typeFormatFlags|checker.TypeFormatFlagsInTypeAlias))
156+
b.WriteString(c.TypeToStringEx(c.GetDeclaredTypeOfSymbol(symbol), container, typeFormatFlags|checker.TypeFormatFlagsInTypeAlias))
145157
}
146158
declaration = core.Find(symbol.Declarations, ast.IsTypeAliasDeclaration)
147159
case flags&ast.SymbolFlagsAlias != 0:
148160
b.WriteString("import ")
149-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
161+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
150162
default:
151-
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbol(symbol), nil, typeFormatFlags))
163+
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbol(symbol), container, typeFormatFlags))
152164
}
153165
return b.String(), declaration
154166
}
155167

168+
func getNodeForQuickInfo(node *ast.Node) *ast.Node {
169+
if ast.IsNewExpression(node.Parent) && node.Pos() == node.Parent.Pos() {
170+
return node.Parent.Expression()
171+
}
172+
if ast.IsNamedTupleMember(node.Parent) && node.Pos() == node.Parent.Pos() {
173+
return node.Parent
174+
}
175+
if ast.IsImportMeta(node.Parent) && node.Parent.Name() == node {
176+
return node.Parent
177+
}
178+
if ast.IsJsxNamespacedName(node.Parent) {
179+
return node.Parent
180+
}
181+
return node
182+
}
183+
156184
func inConstructorContext(node *ast.Node) bool {
157185
if node.Kind == ast.KindConstructorKeyword {
158186
return true
@@ -168,6 +196,30 @@ func inConstructorContext(node *ast.Node) bool {
168196
return false
169197
}
170198

199+
func getSignaturesAtLocation(c *checker.Checker, symbol *ast.Symbol, kind checker.SignatureKind, node *ast.Node) []*checker.Signature {
200+
signatures := c.GetSignaturesOfType(c.GetTypeOfSymbol(symbol), kind)
201+
if len(signatures) > 1 || len(signatures) == 1 && len(signatures[0].TypeParameters()) != 0 {
202+
if callNode := getCallOrNewExpression(node); callNode != nil {
203+
signature := c.GetResolvedSignature(callNode)
204+
// If we have a resolved signature, make sure it isn't a synthetic signature
205+
if signature != nil && (slices.Contains(signatures, signature) || signature.Target() != nil && slices.Contains(signatures, signature.Target())) {
206+
return []*checker.Signature{signature}
207+
}
208+
}
209+
}
210+
return signatures
211+
}
212+
213+
func getCallOrNewExpression(node *ast.Node) *ast.Node {
214+
if ast.IsPropertyAccessExpression(node.Parent) && node.Parent.Name() == node {
215+
node = node.Parent
216+
}
217+
if ast.IsCallExpression(node.Parent) || ast.IsNewExpression(node.Parent) {
218+
return node.Parent
219+
}
220+
return nil
221+
}
222+
171223
func writeTypeParams(b *strings.Builder, c *checker.Checker, params []*checker.Type) {
172224
if len(params) > 0 {
173225
b.WriteString("<")
@@ -187,7 +239,7 @@ func writeTypeParams(b *strings.Builder, c *checker.Checker, params []*checker.T
187239
}
188240
}
189241

190-
func writeSignatures(b *strings.Builder, c *checker.Checker, signatures []*checker.Signature, prefix string, symbol *ast.Symbol) {
242+
func writeSignatures(b *strings.Builder, c *checker.Checker, signatures []*checker.Signature, container *ast.Node, prefix string, symbol *ast.Symbol) {
191243
for i, sig := range signatures {
192244
if i != 0 {
193245
b.WriteString("\n")
@@ -197,8 +249,8 @@ func writeSignatures(b *strings.Builder, c *checker.Checker, signatures []*check
197249
break
198250
}
199251
b.WriteString(prefix)
200-
b.WriteString(c.SymbolToStringEx(symbol, nil, ast.SymbolFlagsNone, symbolFormatFlags))
201-
b.WriteString(c.SignatureToStringEx(sig, nil, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature))
252+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
253+
b.WriteString(c.SignatureToStringEx(sig, container, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature|checker.TypeFormatFlagsWriteTypeArgumentsOfSignature))
202254
}
203255
}
204256

internal/printer/printer.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,7 +1404,13 @@ func (p *Printer) emitTypeParameter(node *ast.TypeParameterDeclaration) {
14041404
}
14051405

14061406
func (p *Printer) emitTypeParameterNode(node *ast.TypeParameterDeclarationNode) {
1407-
p.emitTypeParameter(node.AsTypeParameter())
1407+
// NOTE: QuickInfo uses TypeFormatFlagsWriteTypeArgumentsOfSignature to instruct the NodeBuilder to store type arguments
1408+
// (i.e. type nodes) instead of type parameter declarations in the type parameter list.
1409+
if ast.IsTypeParameterDeclaration(node) {
1410+
p.emitTypeParameter(node.AsTypeParameter())
1411+
} else {
1412+
p.emitTypeArgument(node)
1413+
}
14081414
}
14091415

14101416
func (p *Printer) emitParameterName(node *ast.BindingName) {
@@ -1450,12 +1456,9 @@ func (p *Printer) emitModifierLike(node *ast.ModifierLike) {
14501456
}
14511457

14521458
func (p *Printer) emitTypeParameters(parentNode *ast.Node, nodes *ast.TypeParameterList) {
1453-
// NOTE: for quickinfo, the old emitter emits TypeArguments instead of TypeParameters if they are present. this
1454-
// behavior should be moved to the caller if it is needed
14551459
if nodes == nil {
14561460
return
14571461
}
1458-
14591462
p.emitList((*Printer).emitTypeParameterNode, parentNode, nodes, LFTypeParameters|core.IfElse(ast.IsArrowFunction(parentNode) /*p.shouldAllowTrailingComma(parentNode, nodes)*/, LFAllowTrailingComma, LFNone)) // TODO: preserve trailing comma after Strada migration
14601463
}
14611464

0 commit comments

Comments
 (0)