Skip to content

Commit eb24bb4

Browse files
committed
fix(2260): preserve inherited jsdoc details
1 parent 1d138ea commit eb24bb4

File tree

5 files changed

+121
-52
lines changed

5 files changed

+121
-52
lines changed

internal/ls/hover.go

Lines changed: 88 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -75,58 +75,87 @@ func (l *LanguageService) getDocumentationFromDeclaration(c *checker.Checker, de
7575
return ""
7676
}
7777
isMarkdown := contentFormat == lsproto.MarkupKindMarkdown
78+
79+
jsdoc := getJSDocOrTag(c, declaration)
80+
if jsdoc == nil {
81+
return l.getDocumentation(c, getInheritedJSDocOrTag(c, declaration), isMarkdown)
82+
}
83+
if containsTypedefTag(jsdoc) {
84+
return ""
85+
}
86+
7887
var b strings.Builder
79-
if jsdoc := getJSDocOrTag(c, declaration); jsdoc != nil && !containsTypedefTag(jsdoc) {
80-
l.writeComments(&b, c, jsdoc.Comments(), isMarkdown)
81-
if jsdoc.Kind == ast.KindJSDoc {
82-
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
83-
for _, tag := range tags.Nodes {
84-
if tag.Kind == ast.KindJSDocTypeTag {
85-
continue
86-
}
87-
b.WriteString("\n\n")
88-
if isMarkdown {
89-
b.WriteString("*@")
90-
b.WriteString(tag.TagName().Text())
91-
b.WriteString("*")
92-
} else {
93-
b.WriteString("@")
94-
b.WriteString(tag.TagName().Text())
95-
}
96-
switch tag.Kind {
97-
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
98-
writeOptionalEntityName(&b, tag.Name())
99-
case ast.KindJSDocAugmentsTag:
100-
writeOptionalEntityName(&b, tag.ClassName())
101-
case ast.KindJSDocSeeTag:
102-
writeOptionalEntityName(&b, tag.AsJSDocSeeTag().NameExpression)
103-
case ast.KindJSDocTemplateTag:
104-
for i, tp := range tag.TypeParameters() {
105-
if i != 0 {
106-
b.WriteString(",")
107-
}
108-
writeOptionalEntityName(&b, tp.Name())
88+
89+
if jsdoc.Kind == ast.KindJSDoc {
90+
tags := jsdoc.AsJSDoc().Tags
91+
if len(jsdoc.AsJSDoc().Comments()) == 0 || tags == nil || len(tags.Nodes) == 0 || core.Some(tags.Nodes, isInheritDocTag) {
92+
b.WriteString(l.getDocumentation(c, getInheritedJSDocOrTag(c, declaration), isMarkdown))
93+
}
94+
}
95+
96+
b.WriteString(l.getDocumentation(c, jsdoc, isMarkdown))
97+
return b.String()
98+
}
99+
100+
func (l *LanguageService) getDocumentation(c *checker.Checker, jsdoc *ast.Node, isMarkdown bool) string {
101+
if jsdoc == nil {
102+
return ""
103+
}
104+
105+
var b strings.Builder
106+
l.writeComments(&b, c, jsdoc.Comments(), isMarkdown)
107+
if jsdoc.Kind == ast.KindJSDoc {
108+
if tags := jsdoc.AsJSDoc().Tags; tags != nil {
109+
for _, tag := range tags.Nodes {
110+
if tag.Kind == ast.KindJSDocTypeTag {
111+
continue
112+
}
113+
b.WriteString("\n\n")
114+
if isMarkdown {
115+
b.WriteString("*@")
116+
b.WriteString(tag.TagName().Text())
117+
b.WriteString("*")
118+
} else {
119+
b.WriteString("@")
120+
b.WriteString(tag.TagName().Text())
121+
}
122+
switch tag.Kind {
123+
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
124+
writeOptionalEntityName(&b, tag.Name())
125+
case ast.KindJSDocAugmentsTag:
126+
writeOptionalEntityName(&b, tag.ClassName())
127+
case ast.KindJSDocSeeTag:
128+
writeOptionalEntityName(&b, tag.AsJSDocSeeTag().NameExpression)
129+
case ast.KindJSDocTemplateTag:
130+
for i, tp := range tag.TypeParameters() {
131+
if i != 0 {
132+
b.WriteString(",")
109133
}
134+
writeOptionalEntityName(&b, tp.Name())
110135
}
111-
comments := tag.Comments()
112-
if len(comments) != 0 {
113-
if commentHasPrefix(comments, "```") {
114-
b.WriteString("\n")
115-
} else {
116-
b.WriteString(" ")
117-
if !commentHasPrefix(comments, "-") {
118-
b.WriteString("— ")
119-
}
136+
}
137+
comments := tag.Comments()
138+
if len(comments) != 0 {
139+
if commentHasPrefix(comments, "```") {
140+
b.WriteString("\n")
141+
} else {
142+
b.WriteString(" ")
143+
if !commentHasPrefix(comments, "-") {
144+
b.WriteString("— ")
120145
}
121-
l.writeComments(&b, c, comments, isMarkdown)
122146
}
147+
l.writeComments(&b, c, comments, isMarkdown)
123148
}
124149
}
125150
}
126151
}
127152
return b.String()
128153
}
129154

155+
func isInheritDocTag(tag *ast.JSDocTag) bool {
156+
return tag.TagName().Text() == "inheritDoc" || tag.TagName().Text() == "inheritdoc"
157+
}
158+
130159
func formatQuickInfo(quickInfo string) string {
131160
var b strings.Builder
132161
b.Grow(32)
@@ -425,17 +454,33 @@ func getJSDocOrTag(c *checker.Checker, node *ast.Node) *ast.Node {
425454
(ast.IsVariableDeclaration(node.Parent) || ast.IsPropertyDeclaration(node.Parent) || ast.IsPropertyAssignment(node.Parent)) && node.Parent.Initializer() == node:
426455
return getJSDocOrTag(c, node.Parent)
427456
}
457+
return nil
458+
}
459+
460+
func getInheritedJSDocOrTag(c *checker.Checker, node *ast.Node) *ast.Node {
461+
if ast.IsGetAccessorDeclaration(node) || ast.IsSetAccessorDeclaration(node) {
462+
return nil
463+
}
428464
if symbol := node.Symbol(); symbol != nil && node.Parent != nil && ast.IsClassOrInterfaceLike(node.Parent) {
429465
isStatic := ast.HasStaticModifier(node)
430466
for _, baseType := range c.GetBaseTypes(c.GetDeclaredTypeOfSymbol(node.Parent.Symbol())) {
431467
t := baseType
432-
if isStatic {
468+
if isStatic && baseType.Symbol() != nil {
433469
t = c.GetTypeOfSymbol(baseType.Symbol())
434470
}
435471
if prop := c.GetPropertyOfType(t, symbol.Name); prop != nil && prop.ValueDeclaration != nil {
436-
if jsDoc := getJSDocOrTag(c, prop.ValueDeclaration); jsDoc != nil {
437-
return jsDoc
472+
jsdoc := getJSDocOrTag(c, prop.ValueDeclaration)
473+
if jsdoc == nil {
474+
return getInheritedJSDocOrTag(c, prop.ValueDeclaration)
475+
}
476+
tags := jsdoc.AsJSDoc().Tags
477+
if tags == nil {
478+
return jsdoc
479+
}
480+
if containsTypedefTag(jsdoc) {
481+
return nil
438482
}
483+
return jsdoc
439484
}
440485
}
441486
}

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
// | ```tsx
4141
// | (method) SubClass.doSomethingUseful(mySpecificStuff?: { tiger: string; lion: string; }): string
4242
// | ```
43+
// | Useful description always applicable
44+
// |
45+
// | *@returns* — Useful description of return value always applicable.
4346
// |
4447
// |
4548
// | *@inheritDoc*
@@ -65,6 +68,12 @@
6568
// | ```tsx
6669
// | (method) SubClass.func1(stuff1: any): void
6770
// | ```
71+
// | BaseClass.func1
72+
// |
73+
// | *@param* `stuff1` — BaseClass.func1.stuff1
74+
// |
75+
// |
76+
// | *@returns* — BaseClass.func1.returns
6877
// |
6978
// |
7079
// | *@inheritDoc*
@@ -88,7 +97,7 @@
8897
// | ```tsx
8998
// | (property) SubClass.someProperty: string
9099
// | ```
91-
// | text over tag
100+
// | Applicable description always.text over tag
92101
// |
93102
// | *@inheritDoc* — text after tag
94103
// |
@@ -108,7 +117,7 @@
108117
"item": {
109118
"contents": {
110119
"kind": "markdown",
111-
"value": "```tsx\n(method) SubClass.doSomethingUseful(mySpecificStuff?: { tiger: string; lion: string; }): string\n```\n\n\n*@inheritDoc*\n\n*@param* `mySpecificStuff` — Description of my specific parameter.\n"
120+
"value": "```tsx\n(method) SubClass.doSomethingUseful(mySpecificStuff?: { tiger: string; lion: string; }): string\n```\nUseful description always applicable\n\n*@returns* — Useful description of return value always applicable.\n\n\n*@inheritDoc*\n\n*@param* `mySpecificStuff` — Description of my specific parameter.\n"
112121
},
113122
"range": {
114123
"start": {
@@ -135,7 +144,7 @@
135144
"item": {
136145
"contents": {
137146
"kind": "markdown",
138-
"value": "```tsx\n(method) SubClass.func1(stuff1: any): void\n```\n\n\n*@inheritDoc*\n\n*@param* `stuff1` — SubClass.func1.stuff1\n\n\n*@returns* — SubClass.func1.returns\n"
147+
"value": "```tsx\n(method) SubClass.func1(stuff1: any): void\n```\nBaseClass.func1\n\n*@param* `stuff1` — BaseClass.func1.stuff1\n\n\n*@returns* — BaseClass.func1.returns\n\n\n*@inheritDoc*\n\n*@param* `stuff1` — SubClass.func1.stuff1\n\n\n*@returns* — SubClass.func1.returns\n"
139148
},
140149
"range": {
141150
"start": {
@@ -162,7 +171,7 @@
162171
"item": {
163172
"contents": {
164173
"kind": "markdown",
165-
"value": "```tsx\n(property) SubClass.someProperty: string\n```\ntext over tag\n\n*@inheritDoc* — text after tag\n"
174+
"value": "```tsx\n(property) SubClass.someProperty: string\n```\nApplicable description always.text over tag\n\n*@inheritDoc* — text after tag\n"
166175
},
167176
"range": {
168177
"start": {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
// | ```tsx
1919
// | (property) SubClass.prop: T
2020
// | ```
21-
// |
21+
// | Base.prop
2222
// |
2323
// | *@inheritdoc* — SubClass.prop
2424
// |
@@ -38,7 +38,7 @@
3838
"item": {
3939
"contents": {
4040
"kind": "markdown",
41-
"value": "```tsx\n(property) SubClass.prop: T\n```\n\n\n*@inheritdoc* — SubClass.prop\n"
41+
"value": "```tsx\n(property) SubClass.prop: T\n```\nBase.prop\n\n*@inheritdoc* — SubClass.prop\n"
4242
},
4343
"range": {
4444
"start": {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
// | ```tsx
2020
// | (property) SubClass.prop: string
2121
// | ```
22-
// |
22+
// | Base.prop
2323
// |
2424
// | *@inheritdoc* — SubClass.prop
2525
// |
@@ -39,7 +39,7 @@
3939
"item": {
4040
"contents": {
4141
"kind": "markdown",
42-
"value": "```tsx\n(property) SubClass.prop: string\n```\n\n\n*@inheritdoc* — SubClass.prop\n"
42+
"value": "```tsx\n(property) SubClass.prop: string\n```\nBase.prop\n\n*@inheritdoc* — SubClass.prop\n"
4343
},
4444
"range": {
4545
"start": {

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

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,21 @@
2222
// | ```tsx
2323
// | (method) Bar.method(x: any, y: any): number
2424
// | ```
25+
// | comment
26+
// |
27+
// | *@author* — Me <me@domain.tld>
28+
// |
29+
// |
30+
// | *@see* `x` — (the parameter)
31+
// |
32+
// |
33+
// | *@param* `x` - x comment
34+
// |
35+
// |
36+
// | *@param* `y` - y comment
37+
// |
38+
// |
39+
// | *@returns* — The result
2540
// |
2641
// |
2742
// | *@inheritDoc*
@@ -44,7 +59,7 @@
4459
"item": {
4560
"contents": {
4661
"kind": "markdown",
47-
"value": "```tsx\n(method) Bar.method(x: any, y: any): number\n```\n\n\n*@inheritDoc*"
62+
"value": "```tsx\n(method) Bar.method(x: any, y: any): number\n```\ncomment\n\n*@author* — Me <me@domain.tld>\n\n\n*@see* `x` — (the parameter)\n\n\n*@param* `x` - x comment\n\n\n*@param* `y` - y comment\n\n\n*@returns* — The result\n\n\n*@inheritDoc*"
4863
},
4964
"range": {
5065
"start": {

0 commit comments

Comments
 (0)