Skip to content

Commit 6ba0ed7

Browse files
authored
Display all symbol meanings in quick info (#2144)
1 parent 63b00de commit 6ba0ed7

File tree

1 file changed

+150
-143
lines changed

1 file changed

+150
-143
lines changed

internal/ls/hover.go

Lines changed: 150 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/microsoft/typescript-go/internal/ast"
1010
"github.com/microsoft/typescript-go/internal/astnav"
1111
"github.com/microsoft/typescript-go/internal/checker"
12+
"github.com/microsoft/typescript-go/internal/collections"
1213
"github.com/microsoft/typescript-go/internal/core"
1314
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
1415
)
@@ -59,6 +60,9 @@ func (l *LanguageService) ProvideHover(ctx context.Context, documentURI lsproto.
5960
}
6061

6162
func (l *LanguageService) getQuickInfoAndDocumentationForSymbol(c *checker.Checker, symbol *ast.Symbol, node *ast.Node, contentFormat lsproto.MarkupKind) (string, string) {
63+
if symbol == nil {
64+
return "", ""
65+
}
6266
quickInfo, declaration := getQuickInfoAndDeclarationAtLocation(c, symbol, node)
6367
if quickInfo == "" {
6468
return "", ""
@@ -131,149 +135,164 @@ func formatQuickInfo(quickInfo string) string {
131135
}
132136

133137
func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol, node *ast.Node) (string, *ast.Node) {
138+
var b strings.Builder
139+
var visitedAliases collections.Set[*ast.Symbol]
134140
container := getContainerNode(node)
135141
if node.Kind == ast.KindThisKeyword && ast.IsInExpressionContext(node) {
136142
return c.TypeToStringEx(c.GetTypeAtLocation(node), container, typeFormatFlags), nil
137143
}
138-
isAlias := symbol != nil && symbol.Flags&ast.SymbolFlagsAlias != 0
139-
if isAlias {
140-
symbol = c.GetAliasedSymbol(symbol)
141-
}
142-
if symbol == nil || symbol == c.GetUnknownSymbol() {
143-
return "", nil
144-
}
145-
declaration := symbol.ValueDeclaration
146-
flags := symbol.Flags
147-
if flags&ast.SymbolFlagsProperty != 0 && declaration != nil && ast.IsMethodDeclaration(declaration) {
148-
flags = ast.SymbolFlagsMethod
149-
}
150-
if flags&ast.SymbolFlagsType != 0 && (ast.IsPartOfTypeNode(node) || ast.IsTypeDeclarationName(node)) {
151-
// If the symbol has a type meaning and we're in a type context, remove value-only meanings
152-
flags &^= ast.SymbolFlagsVariable | ast.SymbolFlagsFunction
153-
}
154-
var b strings.Builder
155-
if isAlias {
156-
b.WriteString("(alias) ")
157-
}
158-
switch {
159-
case flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty|ast.SymbolFlagsAccessor) != 0:
160-
switch {
161-
case flags&ast.SymbolFlagsProperty != 0:
162-
b.WriteString("(property) ")
163-
case flags&ast.SymbolFlagsAccessor != 0:
164-
b.WriteString("(accessor) ")
165-
default:
166-
decl := symbol.ValueDeclaration
167-
if decl != nil {
168-
switch {
169-
case ast.IsParameter(decl):
170-
b.WriteString("(parameter) ")
171-
case ast.IsVarLet(decl):
172-
b.WriteString("let ")
173-
case ast.IsVarConst(decl):
174-
b.WriteString("const ")
175-
case ast.IsVarUsing(decl):
176-
b.WriteString("using ")
177-
case ast.IsVarAwaitUsing(decl):
178-
b.WriteString("await using ")
179-
default:
180-
b.WriteString("var ")
181-
}
182-
}
144+
writeSymbolMeaning := func(symbol *ast.Symbol, meaning ast.SymbolFlags, isAlias bool) *ast.Node {
145+
flags := symbol.Flags & meaning
146+
if flags == 0 {
147+
return nil
183148
}
184-
if symbol.Name == ast.InternalSymbolNameExportEquals && symbol.Parent != nil && symbol.Parent.Flags&ast.SymbolFlagsModule != 0 {
185-
b.WriteString("exports")
186-
} else {
187-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
149+
declaration := symbol.ValueDeclaration
150+
if flags&ast.SymbolFlagsProperty != 0 && declaration != nil && ast.IsMethodDeclaration(declaration) {
151+
flags = ast.SymbolFlagsMethod
188152
}
189-
b.WriteString(": ")
190-
if callNode := getCallOrNewExpression(node); callNode != nil {
191-
b.WriteString(c.SignatureToStringEx(c.GetResolvedSignature(callNode), container, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature|checker.TypeFormatFlagsWriteTypeArgumentsOfSignature|checker.TypeFormatFlagsWriteArrowStyleSignature))
192-
} else {
193-
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
153+
if b.Len() != 0 {
154+
b.WriteString("\n")
194155
}
195-
case flags&ast.SymbolFlagsEnumMember != 0:
196-
b.WriteString("(enum member) ")
197-
t := c.GetTypeOfSymbol(symbol)
198-
b.WriteString(c.TypeToStringEx(t, container, typeFormatFlags))
199-
if t.Flags()&checker.TypeFlagsLiteral != 0 {
200-
b.WriteString(" = ")
201-
b.WriteString(t.AsLiteralType().String())
156+
if isAlias {
157+
b.WriteString("(alias) ")
202158
}
203-
case flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0:
204-
prefix := core.IfElse(flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
205-
if ast.IsIdentifier(node) && ast.IsFunctionLikeDeclaration(node.Parent) && node.Parent.Name() == node {
206-
declaration = node.Parent
207-
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(declaration)}
208-
writeSignatures(&b, c, signatures, container, prefix, symbol)
209-
} else {
210-
signatures := getSignaturesAtLocation(c, symbol, checker.SignatureKindCall, node)
211-
if len(signatures) == 1 {
212-
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
213-
declaration = d
159+
switch {
160+
case flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty|ast.SymbolFlagsAccessor) != 0:
161+
switch {
162+
case flags&ast.SymbolFlagsProperty != 0:
163+
b.WriteString("(property) ")
164+
case flags&ast.SymbolFlagsAccessor != 0:
165+
b.WriteString("(accessor) ")
166+
default:
167+
decl := symbol.ValueDeclaration
168+
if decl != nil {
169+
switch {
170+
case ast.IsParameter(decl):
171+
b.WriteString("(parameter) ")
172+
case ast.IsVarLet(decl):
173+
b.WriteString("let ")
174+
case ast.IsVarConst(decl):
175+
b.WriteString("const ")
176+
case ast.IsVarUsing(decl):
177+
b.WriteString("using ")
178+
case ast.IsVarAwaitUsing(decl):
179+
b.WriteString("await using ")
180+
default:
181+
b.WriteString("var ")
182+
}
214183
}
215184
}
216-
writeSignatures(&b, c, signatures, container, prefix, symbol)
217-
}
218-
case flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
219-
if node.Kind == ast.KindThisKeyword || ast.IsThisInTypeQuery(node) {
220-
b.WriteString("this")
221-
} else if node.Kind == ast.KindConstructorKeyword && (ast.IsConstructorDeclaration(node.Parent) || ast.IsConstructSignatureDeclaration(node.Parent)) {
222-
declaration = node.Parent
223-
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(declaration)}
224-
writeSignatures(&b, c, signatures, container, "constructor ", symbol)
225-
} else {
226-
var signatures []*checker.Signature
227-
if flags&ast.SymbolFlagsClass != 0 && getCallOrNewExpression(node) != nil {
228-
signatures = getSignaturesAtLocation(c, symbol, checker.SignatureKindConstruct, node)
185+
if symbol.Name == ast.InternalSymbolNameExportEquals && symbol.Parent != nil && symbol.Parent.Flags&ast.SymbolFlagsModule != 0 {
186+
b.WriteString("exports")
187+
} else {
188+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
189+
}
190+
b.WriteString(": ")
191+
if callNode := getCallOrNewExpression(node); callNode != nil {
192+
b.WriteString(c.SignatureToStringEx(c.GetResolvedSignature(callNode), container, typeFormatFlags|checker.TypeFormatFlagsWriteCallStyleSignature|checker.TypeFormatFlagsWriteTypeArgumentsOfSignature|checker.TypeFormatFlagsWriteArrowStyleSignature))
193+
} else {
194+
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
229195
}
230-
if len(signatures) == 1 {
231-
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
232-
declaration = d
196+
case flags&ast.SymbolFlagsEnumMember != 0:
197+
b.WriteString("(enum member) ")
198+
t := c.GetTypeOfSymbol(symbol)
199+
b.WriteString(c.TypeToStringEx(t, container, typeFormatFlags))
200+
if t.Flags()&checker.TypeFlagsLiteral != 0 {
201+
b.WriteString(" = ")
202+
b.WriteString(t.AsLiteralType().String())
203+
}
204+
case flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0:
205+
prefix := core.IfElse(flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
206+
if ast.IsIdentifier(node) && ast.IsFunctionLikeDeclaration(node.Parent) && node.Parent.Name() == node {
207+
declaration = node.Parent
208+
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(declaration)}
209+
writeSignatures(&b, c, signatures, container, isAlias, prefix, symbol)
210+
} else {
211+
signatures := getSignaturesAtLocation(c, symbol, checker.SignatureKindCall, node)
212+
if len(signatures) == 1 {
213+
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
214+
declaration = d
215+
}
233216
}
234-
writeSignatures(&b, c, signatures, container, "constructor ", symbol)
217+
writeSignatures(&b, c, signatures, container, isAlias, prefix, symbol)
218+
}
219+
case flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
220+
if node.Kind == ast.KindThisKeyword || ast.IsThisInTypeQuery(node) {
221+
b.WriteString("this")
222+
} else if node.Kind == ast.KindConstructorKeyword && (ast.IsConstructorDeclaration(node.Parent) || ast.IsConstructSignatureDeclaration(node.Parent)) {
223+
declaration = node.Parent
224+
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(declaration)}
225+
writeSignatures(&b, c, signatures, container, isAlias, "constructor ", symbol)
235226
} else {
236-
b.WriteString(core.IfElse(flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
237-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
238-
params := c.GetDeclaredTypeOfSymbol(symbol).AsInterfaceType().LocalTypeParameters()
239-
writeTypeParams(&b, c, params)
227+
var signatures []*checker.Signature
228+
if flags&ast.SymbolFlagsClass != 0 && getCallOrNewExpression(node) != nil {
229+
signatures = getSignaturesAtLocation(c, symbol, checker.SignatureKindConstruct, node)
230+
}
231+
if len(signatures) == 1 {
232+
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
233+
declaration = d
234+
}
235+
writeSignatures(&b, c, signatures, container, isAlias, "constructor ", symbol)
236+
} else {
237+
b.WriteString(core.IfElse(flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
238+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
239+
params := c.GetDeclaredTypeOfSymbol(symbol).AsInterfaceType().LocalTypeParameters()
240+
writeTypeParams(&b, c, params)
241+
}
240242
}
243+
if flags&ast.SymbolFlagsInterface != 0 {
244+
declaration = core.Find(symbol.Declarations, ast.IsInterfaceDeclaration)
245+
}
246+
case flags&ast.SymbolFlagsEnum != 0:
247+
b.WriteString("enum ")
248+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
249+
case flags&ast.SymbolFlagsModule != 0:
250+
b.WriteString(core.IfElse(symbol.ValueDeclaration != nil && ast.IsSourceFile(symbol.ValueDeclaration), "module ", "namespace "))
251+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
252+
case flags&ast.SymbolFlagsTypeParameter != 0:
253+
b.WriteString("(type parameter) ")
254+
tp := c.GetDeclaredTypeOfSymbol(symbol)
255+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
256+
cons := c.GetConstraintOfTypeParameter(tp)
257+
if cons != nil {
258+
b.WriteString(" extends ")
259+
b.WriteString(c.TypeToStringEx(cons, container, typeFormatFlags))
260+
}
261+
declaration = core.Find(symbol.Declarations, ast.IsTypeParameterDeclaration)
262+
case flags&ast.SymbolFlagsTypeAlias != 0:
263+
b.WriteString("type ")
264+
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
265+
writeTypeParams(&b, c, c.GetTypeAliasTypeParameters(symbol))
266+
if len(symbol.Declarations) != 0 {
267+
b.WriteString(" = ")
268+
b.WriteString(c.TypeToStringEx(c.GetDeclaredTypeOfSymbol(symbol), container, typeFormatFlags|checker.TypeFormatFlagsInTypeAlias))
269+
}
270+
declaration = core.Find(symbol.Declarations, ast.IsTypeAliasDeclaration)
271+
default:
272+
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbol(symbol), container, typeFormatFlags))
241273
}
242-
if flags&ast.SymbolFlagsInterface != 0 {
243-
declaration = core.Find(symbol.Declarations, ast.IsInterfaceDeclaration)
244-
}
245-
case flags&ast.SymbolFlagsEnum != 0:
246-
b.WriteString("enum ")
247-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
248-
case flags&ast.SymbolFlagsModule != 0:
249-
b.WriteString(core.IfElse(symbol.ValueDeclaration != nil && ast.IsSourceFile(symbol.ValueDeclaration), "module ", "namespace "))
250-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
251-
case flags&ast.SymbolFlagsTypeParameter != 0:
252-
b.WriteString("(type parameter) ")
253-
tp := c.GetDeclaredTypeOfSymbol(symbol)
254-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
255-
cons := c.GetConstraintOfTypeParameter(tp)
256-
if cons != nil {
257-
b.WriteString(" extends ")
258-
b.WriteString(c.TypeToStringEx(cons, container, typeFormatFlags))
259-
}
260-
declaration = core.Find(symbol.Declarations, ast.IsTypeParameterDeclaration)
261-
case flags&ast.SymbolFlagsTypeAlias != 0:
262-
b.WriteString("type ")
263-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
264-
writeTypeParams(&b, c, c.GetTypeAliasTypeParameters(symbol))
265-
if len(symbol.Declarations) != 0 {
266-
b.WriteString(" = ")
267-
b.WriteString(c.TypeToStringEx(c.GetDeclaredTypeOfSymbol(symbol), container, typeFormatFlags|checker.TypeFormatFlagsInTypeAlias))
274+
return declaration
275+
}
276+
var writeSymbol func(*ast.Symbol, bool) *ast.Node
277+
writeSymbol = func(symbol *ast.Symbol, isAlias bool) *ast.Node {
278+
var declaration *ast.Node
279+
// Recursively write all meanings of alias
280+
if symbol.Flags&ast.SymbolFlagsAlias != 0 && visitedAliases.AddIfAbsent(symbol) {
281+
if aliasedSymbol := c.GetAliasedSymbol(symbol); aliasedSymbol != c.GetUnknownSymbol() {
282+
declaration = writeSymbol(aliasedSymbol, true /*isAlias*/)
283+
}
268284
}
269-
declaration = core.Find(symbol.Declarations, ast.IsTypeAliasDeclaration)
270-
case flags&ast.SymbolFlagsAlias != 0:
271-
b.WriteString("import ")
272-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
273-
default:
274-
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbol(symbol), container, typeFormatFlags))
275-
}
276-
return b.String(), declaration
285+
// Write the value meaning, if any
286+
declaration = core.OrElse(declaration, writeSymbolMeaning(symbol, ast.SymbolFlagsValue|ast.SymbolFlagsSignature, isAlias))
287+
// Write the type meaning, if any
288+
declaration = core.OrElse(declaration, writeSymbolMeaning(symbol, ast.SymbolFlagsType&^ast.SymbolFlagsValue, isAlias))
289+
// Write the namespace meaning, if any
290+
declaration = core.OrElse(declaration, writeSymbolMeaning(symbol, ast.SymbolFlagsNamespace&^ast.SymbolFlagsValue, isAlias))
291+
// Return the first declaration
292+
return declaration
293+
}
294+
firstDeclaration := writeSymbol(symbol, false /*isAlias*/)
295+
return b.String(), firstDeclaration
277296
}
278297

279298
func getNodeForQuickInfo(node *ast.Node) *ast.Node {
@@ -306,21 +325,6 @@ func getSymbolAtLocationForQuickInfo(c *checker.Checker, node *ast.Node) *ast.Sy
306325
return c.GetSymbolAtLocation(node)
307326
}
308327

309-
func inConstructorContext(node *ast.Node) bool {
310-
if node.Kind == ast.KindConstructorKeyword {
311-
return true
312-
}
313-
if ast.IsIdentifier(node) {
314-
for ast.IsRightSideOfQualifiedNameOrPropertyAccess(node) {
315-
node = node.Parent
316-
}
317-
if ast.IsNewExpression(node.Parent) {
318-
return true
319-
}
320-
}
321-
return false
322-
}
323-
324328
func getSignaturesAtLocation(c *checker.Checker, symbol *ast.Symbol, kind checker.SignatureKind, node *ast.Node) []*checker.Signature {
325329
signatures := c.GetSignaturesOfType(c.GetTypeOfSymbol(symbol), kind)
326330
if len(signatures) > 1 || len(signatures) == 1 && len(signatures[0].TypeParameters()) != 0 {
@@ -367,10 +371,13 @@ func writeTypeParams(b *strings.Builder, c *checker.Checker, params []*checker.T
367371
}
368372
}
369373

370-
func writeSignatures(b *strings.Builder, c *checker.Checker, signatures []*checker.Signature, container *ast.Node, prefix string, symbol *ast.Symbol) {
374+
func writeSignatures(b *strings.Builder, c *checker.Checker, signatures []*checker.Signature, container *ast.Node, isAlias bool, prefix string, symbol *ast.Symbol) {
371375
for i, sig := range signatures {
372376
if i != 0 {
373377
b.WriteString("\n")
378+
if isAlias {
379+
b.WriteString("(alias) ")
380+
}
374381
}
375382
if i == 3 && len(signatures) >= 5 {
376383
b.WriteString(fmt.Sprintf("// +%v more overloads", len(signatures)-3))

0 commit comments

Comments
 (0)