Skip to content

Commit c846370

Browse files
authored
Display inherited JSDoc documentation in quick info (#2111)
1 parent a820457 commit c846370

19 files changed

+240
-140
lines changed

internal/checker/exports.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,3 +182,7 @@ func (c *Checker) ResolveName(name string, location *ast.Node, meaning ast.Symbo
182182
func (c *Checker) GetSymbolFlags(symbol *ast.Symbol) ast.SymbolFlags {
183183
return c.getSymbolFlags(symbol)
184184
}
185+
186+
func (c *Checker) GetBaseTypes(t *Type) []*Type {
187+
return c.getBaseTypes(t)
188+
}

internal/fourslash/_scripts/manualTests.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ completionsAtIncompleteObjectLiteralProperty
33
completionsSelfDeclaring1
44
completionsWithDeprecatedTag4
55
parserCorruptionAfterMapInClass
6+
quickInfoForOverloadOnConst1
67
renameDefaultKeyword
78
renameForDefaultExport01
89
tsxCompletion12

internal/fourslash/tests/gen/quickInfoForOverloadOnConst1_test.go renamed to internal/fourslash/tests/manual/quickInfoForOverloadOnConst1_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ c.x1(1, (x/*10*/x) => { return 1; } );`
3131
f.VerifyQuickInfoAt(t, "1", "(method) I.x1(a: number, callback: (x: \"hi\") => number): any", "")
3232
f.VerifyQuickInfoAt(t, "2", "(method) C.x1(a: number, callback: (x: \"hi\") => number): any", "")
3333
f.VerifyQuickInfoAt(t, "3", "(parameter) callback: (x: \"hi\") => number", "")
34-
f.VerifyQuickInfoAt(t, "4", "(method) C.x1(a: number, callback: (x: \"hi\") => number): any", "")
34+
f.VerifyQuickInfoAt(t, "4", "(method) C.x1(a: number, callback: (x: string) => number): void", "")
3535
f.VerifyQuickInfoAt(t, "5", "(parameter) callback: (x: string) => number", "")
3636
f.VerifyQuickInfoAt(t, "6", "(parameter) callback: (x: string) => number", "")
3737
f.VerifyQuickInfoAt(t, "7", "(method) C.x1(a: number, callback: (x: \"hi\") => number): any", "")

internal/ls/hover.go

Lines changed: 53 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func (l *LanguageService) getDocumentationFromDeclaration(c *checker.Checker, de
7272
}
7373
isMarkdown := contentFormat == lsproto.MarkupKindMarkdown
7474
var b strings.Builder
75-
if jsdoc := getJSDocOrTag(declaration); jsdoc != nil && !containsTypedefTag(jsdoc) {
75+
if jsdoc := getJSDocOrTag(c, declaration); jsdoc != nil && !containsTypedefTag(jsdoc) {
7676
l.writeComments(&b, c, jsdoc.Comments(), isMarkdown)
7777
if jsdoc.Kind == ast.KindJSDoc {
7878
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
@@ -143,14 +143,6 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
143143
return "", nil
144144
}
145145
declaration := symbol.ValueDeclaration
146-
if symbol.Flags&ast.SymbolFlagsClass != 0 && inConstructorContext(node) {
147-
if s := symbol.Members[ast.InternalSymbolNameConstructor]; s != nil {
148-
symbol = s
149-
declaration = core.Find(symbol.Declarations, func(d *ast.Node) bool {
150-
return ast.IsConstructorDeclaration(d) || ast.IsConstructSignatureDeclaration(d)
151-
})
152-
}
153-
}
154146
flags := symbol.Flags
155147
if flags&ast.SymbolFlagsProperty != 0 && declaration != nil && ast.IsMethodDeclaration(declaration) {
156148
flags = ast.SymbolFlagsMethod
@@ -209,30 +201,43 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
209201
b.WriteString(t.AsLiteralType().String())
210202
}
211203
case flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0:
212-
signatures := getSignaturesAtLocation(c, symbol, checker.SignatureKindCall, node)
213-
if len(signatures) == 1 {
214-
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
215-
declaration = d
216-
}
217-
}
218204
prefix := core.IfElse(flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
219-
writeSignatures(&b, c, signatures, container, prefix, symbol)
220-
case flags&ast.SymbolFlagsConstructor != 0:
221-
signatures := getSignaturesAtLocation(c, symbol.Parent, checker.SignatureKindConstruct, node)
222-
if len(signatures) == 1 {
223-
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
224-
declaration = d
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
214+
}
225215
}
216+
writeSignatures(&b, c, signatures, container, prefix, symbol)
226217
}
227-
writeSignatures(&b, c, signatures, container, "constructor ", symbol.Parent)
228218
case flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0:
229219
if node.Kind == ast.KindThisKeyword || ast.IsThisInTypeQuery(node) {
230220
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)
231225
} else {
232-
b.WriteString(core.IfElse(flags&ast.SymbolFlagsClass != 0, "class ", "interface "))
233-
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
234-
params := c.GetDeclaredTypeOfSymbol(symbol).AsInterfaceType().LocalTypeParameters()
235-
writeTypeParams(&b, c, params)
226+
var signatures []*checker.Signature
227+
if flags&ast.SymbolFlagsClass != 0 && getCallOrNewExpression(node) != nil {
228+
signatures = getSignaturesAtLocation(c, symbol, checker.SignatureKindConstruct, node)
229+
}
230+
if len(signatures) == 1 {
231+
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
232+
declaration = d
233+
}
234+
writeSignatures(&b, c, signatures, container, "constructor ", symbol)
235+
} 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)
240+
}
236241
}
237242
if flags&ast.SymbolFlagsInterface != 0 {
238243
declaration = core.Find(symbol.Declarations, ast.IsInterfaceDeclaration)
@@ -381,7 +386,7 @@ func containsTypedefTag(jsdoc *ast.Node) bool {
381386
if jsdoc.Kind == ast.KindJSDoc {
382387
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
383388
for _, tag := range tags.Nodes {
384-
if tag.Kind == ast.KindJSDocTypedefTag {
389+
if tag.Kind == ast.KindJSDocTypedefTag || tag.Kind == ast.KindJSDocCallbackTag {
385390
return true
386391
}
387392
}
@@ -398,26 +403,40 @@ func getJSDoc(node *ast.Node) *ast.Node {
398403
return core.LastOrNil(node.JSDoc(nil))
399404
}
400405

401-
func getJSDocOrTag(node *ast.Node) *ast.Node {
406+
func getJSDocOrTag(c *checker.Checker, node *ast.Node) *ast.Node {
402407
if jsdoc := getJSDoc(node); jsdoc != nil {
403408
return jsdoc
404409
}
405410
switch {
406411
case ast.IsParameter(node):
407-
return getMatchingJSDocTag(node.Parent, node.Name().Text(), isMatchingParameterTag)
412+
return getMatchingJSDocTag(c, node.Parent, node.Name().Text(), isMatchingParameterTag)
408413
case ast.IsTypeParameterDeclaration(node):
409-
return getMatchingJSDocTag(node.Parent, node.Name().Text(), isMatchingTemplateTag)
414+
return getMatchingJSDocTag(c, node.Parent, node.Name().Text(), isMatchingTemplateTag)
410415
case ast.IsVariableDeclaration(node) && ast.IsVariableDeclarationList(node.Parent) && core.FirstOrNil(node.Parent.AsVariableDeclarationList().Declarations.Nodes) == node:
411-
return getJSDocOrTag(node.Parent.Parent)
416+
return getJSDocOrTag(c, node.Parent.Parent)
412417
case (ast.IsFunctionExpressionOrArrowFunction(node) || ast.IsClassExpression(node)) &&
413418
(ast.IsVariableDeclaration(node.Parent) || ast.IsPropertyDeclaration(node.Parent) || ast.IsPropertyAssignment(node.Parent)) && node.Parent.Initializer() == node:
414-
return getJSDocOrTag(node.Parent)
419+
return getJSDocOrTag(c, node.Parent)
420+
}
421+
if symbol := node.Symbol(); symbol != nil && node.Parent != nil && ast.IsClassOrInterfaceLike(node.Parent) {
422+
isStatic := ast.HasStaticModifier(node)
423+
for _, baseType := range c.GetBaseTypes(c.GetDeclaredTypeOfSymbol(node.Parent.Symbol())) {
424+
t := baseType
425+
if isStatic {
426+
t = c.GetTypeOfSymbol(baseType.Symbol())
427+
}
428+
if prop := c.GetPropertyOfType(t, symbol.Name); prop != nil && prop.ValueDeclaration != nil {
429+
if jsDoc := getJSDocOrTag(c, prop.ValueDeclaration); jsDoc != nil {
430+
return jsDoc
431+
}
432+
}
433+
}
415434
}
416435
return nil
417436
}
418437

419-
func getMatchingJSDocTag(node *ast.Node, name string, match func(*ast.Node, string) bool) *ast.Node {
420-
if jsdoc := getJSDocOrTag(node); jsdoc != nil && jsdoc.Kind == ast.KindJSDoc {
438+
func getMatchingJSDocTag(c *checker.Checker, node *ast.Node, name string, match func(*ast.Node, string) bool) *ast.Node {
439+
if jsdoc := getJSDocOrTag(c, node); jsdoc != nil && jsdoc.Kind == ast.KindJSDoc {
421440
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
422441
for _, tag := range tags.Nodes {
423442
if match(tag, name) {

testdata/baselines/reference/fourslash/quickInfo/jsdocOnInheritedMembers1.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// | ```tsx
1919
// | (method) B.method(): void
2020
// | ```
21-
// |
21+
// | Method documentation.
2222
// | ----------------------------------------------------------------------
2323
[
2424
{
@@ -34,7 +34,7 @@
3434
"item": {
3535
"contents": {
3636
"kind": "markdown",
37-
"value": "```tsx\n(method) B.method(): void\n```\n"
37+
"value": "```tsx\n(method) B.method(): void\n```\nMethod documentation."
3838
},
3939
"range": {
4040
"start": {

testdata/baselines/reference/fourslash/quickInfo/jsdocOnInheritedMembers2.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// | ```tsx
1919
// | (method) B.method(): void
2020
// | ```
21-
// |
21+
// | Method documentation.
2222
// | ----------------------------------------------------------------------
2323
[
2424
{
@@ -34,7 +34,7 @@
3434
"item": {
3535
"contents": {
3636
"kind": "markdown",
37-
"value": "```tsx\n(method) B.method(): void\n```\n"
37+
"value": "```tsx\n(method) B.method(): void\n```\nMethod documentation."
3838
},
3939
"range": {
4040
"start": {

testdata/baselines/reference/fourslash/quickInfo/quickInfoCommentsClass.baseline

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
// ^^
2222
// | ----------------------------------------------------------------------
2323
// | ```tsx
24-
// | class c2
24+
// | constructor c2(): c2
2525
// | ```
2626
// | This is class c2 without constructor
2727
// | ----------------------------------------------------------------------
@@ -147,7 +147,7 @@
147147
// ^^
148148
// | ----------------------------------------------------------------------
149149
// | ```tsx
150-
// | class c5
150+
// | constructor c5(): c5
151151
// | ```
152152
// | Class with statics
153153
// | ----------------------------------------------------------------------
@@ -307,7 +307,7 @@
307307
"item": {
308308
"contents": {
309309
"kind": "markdown",
310-
"value": "```tsx\nclass c2\n```\nThis is class c2 without constructor"
310+
"value": "```tsx\nconstructor c2(): c2\n```\nThis is class c2 without constructor"
311311
},
312312
"range": {
313313
"start": {
@@ -712,7 +712,7 @@
712712
"item": {
713713
"contents": {
714714
"kind": "markdown",
715-
"value": "```tsx\nclass c5\n```\nClass with statics"
715+
"value": "```tsx\nconstructor c5(): c5\n```\nClass with statics"
716716
},
717717
"range": {
718718
"start": {

testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsClass.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// ^
2121
// | ----------------------------------------------------------------------
2222
// | ```tsx
23-
// | class c
23+
// | constructor c(): c
2424
// | ```
2525
// |
2626
// | ----------------------------------------------------------------------
@@ -107,7 +107,7 @@
107107
"item": {
108108
"contents": {
109109
"kind": "markdown",
110-
"value": "```tsx\nclass c\n```\n"
110+
"value": "```tsx\nconstructor c(): c\n```\n"
111111
},
112112
"range": {
113113
"start": {

testdata/baselines/reference/fourslash/quickInfo/quickInfoDisplayPartsClassConstructor.baseline

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -47,15 +47,13 @@
4747
// | ----------------------------------------------------------------------
4848
// | ```tsx
4949
// | constructor cWithOverloads(x: string): cWithOverloads
50-
// | constructor cWithOverloads(x: number): cWithOverloads
5150
// | ```
5251
// |
5352
// | ----------------------------------------------------------------------
5453
// constructor(x: number);
5554
// ^^^^^^^^^^^
5655
// | ----------------------------------------------------------------------
5756
// | ```tsx
58-
// | constructor cWithOverloads(x: string): cWithOverloads
5957
// | constructor cWithOverloads(x: number): cWithOverloads
6058
// | ```
6159
// |
@@ -64,8 +62,7 @@
6462
// ^^^^^^^^^^^
6563
// | ----------------------------------------------------------------------
6664
// | ```tsx
67-
// | constructor cWithOverloads(x: string): cWithOverloads
68-
// | constructor cWithOverloads(x: number): cWithOverloads
65+
// | constructor cWithOverloads(x: any): cWithOverloads
6966
// | ```
7067
// |
7168
// | ----------------------------------------------------------------------
@@ -122,27 +119,21 @@
122119
// | ----------------------------------------------------------------------
123120
// | ```tsx
124121
// | constructor cWithMultipleOverloads(x: string): cWithMultipleOverloads
125-
// | constructor cWithMultipleOverloads(x: number): cWithMultipleOverloads
126-
// | constructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads
127122
// | ```
128123
// |
129124
// | ----------------------------------------------------------------------
130125
// constructor(x: number);
131126
// ^^^^^^^^^^^
132127
// | ----------------------------------------------------------------------
133128
// | ```tsx
134-
// | constructor cWithMultipleOverloads(x: string): cWithMultipleOverloads
135129
// | constructor cWithMultipleOverloads(x: number): cWithMultipleOverloads
136-
// | constructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads
137130
// | ```
138131
// |
139132
// | ----------------------------------------------------------------------
140133
// constructor(x: boolean);
141134
// ^^^^^^^^^^^
142135
// | ----------------------------------------------------------------------
143136
// | ```tsx
144-
// | constructor cWithMultipleOverloads(x: string): cWithMultipleOverloads
145-
// | constructor cWithMultipleOverloads(x: number): cWithMultipleOverloads
146137
// | constructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads
147138
// | ```
148139
// |
@@ -151,9 +142,7 @@
151142
// ^^^^^^^^^^^
152143
// | ----------------------------------------------------------------------
153144
// | ```tsx
154-
// | constructor cWithMultipleOverloads(x: string): cWithMultipleOverloads
155-
// | constructor cWithMultipleOverloads(x: number): cWithMultipleOverloads
156-
// | constructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads
145+
// | constructor cWithMultipleOverloads(x: any): cWithMultipleOverloads
157146
// | ```
158147
// |
159148
// | ----------------------------------------------------------------------
@@ -368,7 +357,7 @@
368357
"item": {
369358
"contents": {
370359
"kind": "markdown",
371-
"value": "```tsx\nconstructor cWithOverloads(x: string): cWithOverloads\nconstructor cWithOverloads(x: number): cWithOverloads\n```\n"
360+
"value": "```tsx\nconstructor cWithOverloads(x: string): cWithOverloads\n```\n"
372361
},
373362
"range": {
374363
"start": {
@@ -395,7 +384,7 @@
395384
"item": {
396385
"contents": {
397386
"kind": "markdown",
398-
"value": "```tsx\nconstructor cWithOverloads(x: string): cWithOverloads\nconstructor cWithOverloads(x: number): cWithOverloads\n```\n"
387+
"value": "```tsx\nconstructor cWithOverloads(x: number): cWithOverloads\n```\n"
399388
},
400389
"range": {
401390
"start": {
@@ -422,7 +411,7 @@
422411
"item": {
423412
"contents": {
424413
"kind": "markdown",
425-
"value": "```tsx\nconstructor cWithOverloads(x: string): cWithOverloads\nconstructor cWithOverloads(x: number): cWithOverloads\n```\n"
414+
"value": "```tsx\nconstructor cWithOverloads(x: any): cWithOverloads\n```\n"
426415
},
427416
"range": {
428417
"start": {
@@ -611,7 +600,7 @@
611600
"item": {
612601
"contents": {
613602
"kind": "markdown",
614-
"value": "```tsx\nconstructor cWithMultipleOverloads(x: string): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: number): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads\n```\n"
603+
"value": "```tsx\nconstructor cWithMultipleOverloads(x: string): cWithMultipleOverloads\n```\n"
615604
},
616605
"range": {
617606
"start": {
@@ -638,7 +627,7 @@
638627
"item": {
639628
"contents": {
640629
"kind": "markdown",
641-
"value": "```tsx\nconstructor cWithMultipleOverloads(x: string): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: number): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads\n```\n"
630+
"value": "```tsx\nconstructor cWithMultipleOverloads(x: number): cWithMultipleOverloads\n```\n"
642631
},
643632
"range": {
644633
"start": {
@@ -665,7 +654,7 @@
665654
"item": {
666655
"contents": {
667656
"kind": "markdown",
668-
"value": "```tsx\nconstructor cWithMultipleOverloads(x: string): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: number): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads\n```\n"
657+
"value": "```tsx\nconstructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads\n```\n"
669658
},
670659
"range": {
671660
"start": {
@@ -692,7 +681,7 @@
692681
"item": {
693682
"contents": {
694683
"kind": "markdown",
695-
"value": "```tsx\nconstructor cWithMultipleOverloads(x: string): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: number): cWithMultipleOverloads\nconstructor cWithMultipleOverloads(x: boolean): cWithMultipleOverloads\n```\n"
684+
"value": "```tsx\nconstructor cWithMultipleOverloads(x: any): cWithMultipleOverloads\n```\n"
696685
},
697686
"range": {
698687
"start": {

0 commit comments

Comments
 (0)