Skip to content

Commit 297d6ea

Browse files
authored
Properly track first Quick Info declaration + fix issue in fourslash (microsoft#2641)
1 parent 4414a06 commit 297d6ea

13 files changed

Lines changed: 937 additions & 67 deletions

internal/fourslash/_scripts/crashingTests.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,4 @@ TestFormatDocumentWithTrivia
55
TestFormattingJsxTexts4
66
TestGetOccurrencesIfElseBroken
77
TestImportNameCodeFix_importType8
8-
TestJsdocLink2
9-
TestJsdocLink3
10-
TestJsdocLink6
11-
TestQuickInfoAlias
128
TestQuickInfoBindingPatternInJsdocNoCrash1

internal/fourslash/_scripts/failingTests.txt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -375,9 +375,6 @@ TestJsDocFunctionSignatures7
375375
TestJsDocFunctionSignatures8
376376
TestJsDocGenerics2
377377
TestJsDocInheritDoc
378-
TestJsdocLink2
379-
TestJsdocLink3
380-
TestJsdocLink6
381378
TestJsDocPropertyDescription1
382379
TestJsDocPropertyDescription10
383380
TestJsDocPropertyDescription11
@@ -430,7 +427,6 @@ TestProtoVarVisibleWithOuterScopeUnderscoreProto
430427
TestQualifyModuleTypeNames
431428
TestQuickInfo_notInsideComment
432429
TestQuickinfo01
433-
TestQuickInfoAlias
434430
TestQuickInfoAssertionNodeNotReusedWhenTypeNotEquivalent1
435431
TestQuickInfoBindingPatternInJsdocNoCrash1
436432
TestQuickInfoCanBeTruncated

internal/fourslash/fourslash.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2101,7 +2101,7 @@ func (f *FourslashTest) VerifyBaselineHover(t *testing.T) {
21012101

21022102
params := &lsproto.HoverParams{
21032103
TextDocument: lsproto.TextDocumentIdentifier{
2104-
Uri: lsconv.FileNameToDocumentURI(f.activeFilename),
2104+
Uri: lsconv.FileNameToDocumentURI(marker.fileName),
21052105
},
21062106
Position: marker.LSPosition,
21072107
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package fourslash_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/microsoft/typescript-go/internal/fourslash"
7+
"github.com/microsoft/typescript-go/internal/testutil"
8+
)
9+
10+
func TestQuickInfoMergedAlias(t *testing.T) {
11+
fourslash.SkipIfFailing(t)
12+
t.Parallel()
13+
defer testutil.RecoverAndFail(t, "Panic on fourslash test")
14+
const content = `// @filename: /a.ts
15+
/**
16+
* A function
17+
*/
18+
export function foo/*1*/() {}
19+
// @filename: /b.ts
20+
import { foo/*2*/ } from './a';
21+
export { foo/*3*/ };
22+
23+
/**
24+
* A type
25+
*/
26+
type foo/*4*/ = number;
27+
28+
foo/*5*/()
29+
let x1: foo/*6*/;
30+
// @filename: /c.ts
31+
import { foo/*7*/ } from './b';
32+
33+
/**
34+
* A namespace
35+
*/
36+
namespace foo/*8*/ {
37+
export type bar = string[];
38+
}
39+
40+
foo/*9*/()
41+
let x1: foo/*10*/;
42+
let x2: foo/*11*/.bar;`
43+
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
44+
defer done()
45+
f.VerifyBaselineHover(t)
46+
}

internal/ls/hover.go

Lines changed: 34 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,12 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
182182
var b strings.Builder
183183
var visitedAliases collections.Set[*ast.Symbol]
184184
var aliasLevel int
185+
var firstDeclaration *ast.Node
186+
setDeclaration := func(declaration *ast.Node) {
187+
if firstDeclaration == nil {
188+
firstDeclaration = declaration
189+
}
190+
}
185191
writeNewLine := func() {
186192
if b.Len() != 0 {
187193
b.WriteString("\n")
@@ -219,14 +225,13 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
219225
b.WriteString(">")
220226
}
221227
}
222-
var writeSymbol func(*ast.Symbol) *ast.Node
223-
writeSymbol = func(symbol *ast.Symbol) *ast.Node {
224-
var declaration *ast.Node
228+
var writeSymbol func(*ast.Symbol)
229+
writeSymbol = func(symbol *ast.Symbol) {
225230
// Recursively write all meanings of alias
226231
if symbol.Flags&ast.SymbolFlagsAlias != 0 && visitedAliases.AddIfAbsent(symbol) {
227232
if aliasedSymbol := c.GetAliasedSymbol(symbol); aliasedSymbol != c.GetUnknownSymbol() {
228233
aliasLevel++
229-
declaration = writeSymbol(aliasedSymbol)
234+
writeSymbol(aliasedSymbol)
230235
aliasLevel--
231236
}
232237
}
@@ -238,19 +243,21 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
238243
flags = symbol.Flags & ast.SymbolFlagsType
239244
case ast.SemanticMeaningNamespace:
240245
flags = symbol.Flags & ast.SymbolFlagsNamespace
246+
default:
247+
flags = symbol.Flags & (ast.SymbolFlagsValue | ast.SymbolFlagsSignature | ast.SymbolFlagsType | ast.SymbolFlagsNamespace)
241248
}
242249
if flags == 0 {
250+
if aliasLevel != 0 || b.Len() != 0 {
251+
return
252+
}
243253
flags = symbol.Flags & (ast.SymbolFlagsValue | ast.SymbolFlagsSignature | ast.SymbolFlagsType | ast.SymbolFlagsNamespace)
244254
if flags == 0 {
245-
return nil
255+
return
246256
}
247257
}
248258
if flags&ast.SymbolFlagsProperty != 0 && symbol.ValueDeclaration != nil && ast.IsMethodDeclaration(symbol.ValueDeclaration) {
249259
flags = ast.SymbolFlagsMethod
250260
}
251-
if flags&ast.SymbolFlagsValue != 0 {
252-
declaration = symbol.ValueDeclaration
253-
}
254261
if flags&(ast.SymbolFlagsVariable|ast.SymbolFlagsProperty|ast.SymbolFlagsAccessor) != 0 {
255262
writeNewLine()
256263
switch {
@@ -288,6 +295,7 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
288295
} else {
289296
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbolAtLocation(symbol, node), container, typeFormatFlags))
290297
}
298+
setDeclaration(symbol.ValueDeclaration)
291299
}
292300
if flags&ast.SymbolFlagsEnumMember != 0 {
293301
writeNewLine()
@@ -298,33 +306,32 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
298306
b.WriteString(" = ")
299307
b.WriteString(t.AsLiteralType().String())
300308
}
309+
setDeclaration(symbol.ValueDeclaration)
301310
}
302311
if flags&(ast.SymbolFlagsFunction|ast.SymbolFlagsMethod) != 0 {
303312
prefix := core.IfElse(flags&ast.SymbolFlagsMethod != 0, "(method) ", "function ")
304313
if ast.IsIdentifier(node) && ast.IsFunctionLikeDeclaration(node.Parent) && node.Parent.Name() == node {
305-
declaration = node.Parent
306-
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(declaration)}
314+
setDeclaration(node.Parent)
315+
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(node.Parent)}
307316
writeSignatures(signatures, prefix, symbol)
308317
} else {
309318
signatures := getSignaturesAtLocation(c, symbol, checker.SignatureKindCall, node)
310319
if len(signatures) == 1 {
311320
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
312-
declaration = d
321+
setDeclaration(d)
313322
}
314323
}
315324
writeSignatures(signatures, prefix, symbol)
316325
}
326+
setDeclaration(symbol.ValueDeclaration)
317327
}
318328
if flags&(ast.SymbolFlagsClass|ast.SymbolFlagsInterface) != 0 {
319-
if flags&ast.SymbolFlagsInterface != 0 && (declaration == nil || ast.IsIdentifier(node) && ast.IsInterfaceDeclaration(node.Parent)) {
320-
declaration = core.Find(symbol.Declarations, ast.IsInterfaceDeclaration)
321-
}
322329
if node.Kind == ast.KindThisKeyword || ast.IsThisInTypeQuery(node) {
323330
writeNewLine()
324331
b.WriteString("this")
325332
} else if node.Kind == ast.KindConstructorKeyword && (ast.IsConstructorDeclaration(node.Parent) || ast.IsConstructSignatureDeclaration(node.Parent)) {
326-
declaration = node.Parent
327-
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(declaration)}
333+
setDeclaration(node.Parent)
334+
signatures := []*checker.Signature{c.GetSignatureFromDeclaration(node.Parent)}
328335
writeSignatures(signatures, "constructor ", symbol)
329336
} else {
330337
var signatures []*checker.Signature
@@ -333,7 +340,7 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
333340
}
334341
if len(signatures) == 1 {
335342
if d := signatures[0].Declaration(); d != nil && d.Flags&ast.NodeFlagsJSDoc == 0 {
336-
declaration = d
343+
setDeclaration(d)
337344
}
338345
writeSignatures(signatures, "constructor ", symbol)
339346
} else {
@@ -344,22 +351,23 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
344351
writeTypeParams(params)
345352
}
346353
}
354+
if flags&ast.SymbolFlagsClass != 0 {
355+
setDeclaration(symbol.ValueDeclaration)
356+
} else {
357+
setDeclaration(core.Find(symbol.Declarations, ast.IsInterfaceDeclaration))
358+
}
347359
}
348360
if flags&ast.SymbolFlagsEnum != 0 {
349361
writeNewLine()
350362
b.WriteString("enum ")
351363
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
352-
if declaration == nil || ast.IsIdentifier(node) && ast.IsEnumDeclaration(node.Parent) {
353-
declaration = core.Find(symbol.Declarations, ast.IsEnumDeclaration)
354-
}
364+
setDeclaration(core.Find(symbol.Declarations, ast.IsEnumDeclaration))
355365
}
356366
if flags&ast.SymbolFlagsModule != 0 {
357367
writeNewLine()
358368
b.WriteString(core.IfElse(symbol.ValueDeclaration != nil && ast.IsSourceFile(symbol.ValueDeclaration), "module ", "namespace "))
359369
b.WriteString(c.SymbolToStringEx(symbol, container, ast.SymbolFlagsNone, symbolFormatFlags))
360-
if declaration == nil || ast.IsIdentifier(node) && ast.IsModuleDeclaration(node.Parent) {
361-
declaration = core.Find(symbol.Declarations, ast.IsModuleDeclaration)
362-
}
370+
setDeclaration(core.Find(symbol.Declarations, ast.IsModuleDeclaration))
363371
}
364372
if flags&ast.SymbolFlagsTypeParameter != 0 {
365373
writeNewLine()
@@ -371,9 +379,7 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
371379
b.WriteString(" extends ")
372380
b.WriteString(c.TypeToStringEx(cons, container, typeFormatFlags))
373381
}
374-
if declaration == nil || ast.IsIdentifier(node) && ast.IsTypeParameterDeclaration(node.Parent) {
375-
declaration = core.Find(symbol.Declarations, ast.IsTypeParameterDeclaration)
376-
}
382+
setDeclaration(core.Find(symbol.Declarations, ast.IsTypeParameterDeclaration))
377383
}
378384
if flags&ast.SymbolFlagsTypeAlias != 0 {
379385
writeNewLine()
@@ -384,17 +390,14 @@ func getQuickInfoAndDeclarationAtLocation(c *checker.Checker, symbol *ast.Symbol
384390
b.WriteString(" = ")
385391
b.WriteString(c.TypeToStringEx(c.GetDeclaredTypeOfSymbol(symbol), container, typeFormatFlags|checker.TypeFormatFlagsInTypeAlias))
386392
}
387-
if declaration == nil || ast.IsIdentifier(node) && ast.IsTypeOrJSTypeAliasDeclaration(node.Parent) {
388-
declaration = core.Find(symbol.Declarations, ast.IsTypeOrJSTypeAliasDeclaration)
389-
}
393+
setDeclaration(core.Find(symbol.Declarations, ast.IsTypeOrJSTypeAliasDeclaration))
390394
}
391395
if flags&ast.SymbolFlagsSignature != 0 {
392396
writeNewLine()
393397
b.WriteString(c.TypeToStringEx(c.GetTypeOfSymbol(symbol), container, typeFormatFlags))
394398
}
395-
return declaration
396399
}
397-
firstDeclaration := writeSymbol(symbol)
400+
writeSymbol(symbol)
398401
return b.String(), firstDeclaration
399402
}
400403

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

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,19 @@
1414
// | ----------------------------------------------------------------------
1515
=== /test.ts ===
1616
// export { default as test } from "./jsDocAliasQuickInfo";
17-
// ^
17+
// ^^^^^^^
1818
// | ----------------------------------------------------------------------
19-
// | No quickinfo at /*2*/.
19+
// | ```tsx
20+
// | (property) default: 10
21+
// | ```
22+
// | Comment
2023
// | ----------------------------------------------------------------------
21-
// ^
24+
// ^^^^
2225
// | ----------------------------------------------------------------------
23-
// | No quickinfo at /*3*/.
26+
// | ```tsx
27+
// | (alias) (property) default: 10
28+
// | ```
29+
// | Comment
2430
// | ----------------------------------------------------------------------
2531

2632

@@ -62,7 +68,22 @@
6268
"Name": "2",
6369
"Data": {}
6470
},
65-
"item": null
71+
"item": {
72+
"contents": {
73+
"kind": "markdown",
74+
"value": "```tsx\n(property) default: 10\n```\nComment"
75+
},
76+
"range": {
77+
"start": {
78+
"line": 0,
79+
"character": 9
80+
},
81+
"end": {
82+
"line": 0,
83+
"character": 16
84+
}
85+
}
86+
}
6687
},
6788
{
6889
"marker": {
@@ -74,6 +95,21 @@
7495
"Name": "3",
7596
"Data": {}
7697
},
77-
"item": null
98+
"item": {
99+
"contents": {
100+
"kind": "markdown",
101+
"value": "```tsx\n(alias) (property) default: 10\n```\nComment"
102+
},
103+
"range": {
104+
"start": {
105+
"line": 0,
106+
"character": 20
107+
},
108+
"end": {
109+
"line": 0,
110+
"character": 24
111+
}
112+
}
113+
}
78114
}
79115
]
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// === QuickInfo ===
2+
=== /script.ts ===
3+
// /**
4+
// * {@link C}
5+
// * @wat Makes a {@link C}. A default one.
6+
// * {@link C()}
7+
// * {@link C|postfix text}
8+
// * {@link unformatted postfix text}
9+
// * @see {@link C} its great
10+
// */
11+
// function CC() {
12+
// ^^
13+
// | ----------------------------------------------------------------------
14+
// | ```tsx
15+
// | function CC(): void
16+
// | ```
17+
// | [C](file:///jsdocLink2.ts#1,7-1,8)
18+
// |
19+
// | *@wat* — Makes a [C](file:///jsdocLink2.ts#1,7-1,8). A default one.
20+
// | [C()](file:///jsdocLink2.ts#1,7-1,8)
21+
// | [postfix text](file:///jsdocLink2.ts#1,7-1,8)
22+
// | unformatted postfix text
23+
// |
24+
// | *@see* — [C](file:///jsdocLink2.ts#1,7-1,8) its great
25+
// |
26+
// | ----------------------------------------------------------------------
27+
// }
28+
[
29+
{
30+
"marker": {
31+
"Position": 177,
32+
"LSPosition": {
33+
"line": 8,
34+
"character": 9
35+
},
36+
"Name": "",
37+
"Data": {}
38+
},
39+
"item": {
40+
"contents": {
41+
"kind": "markdown",
42+
"value": "```tsx\nfunction CC(): void\n```\n[C](file:///jsdocLink2.ts#1,7-1,8)\n\n*@wat* — Makes a [C](file:///jsdocLink2.ts#1,7-1,8). A default one.\n[C()](file:///jsdocLink2.ts#1,7-1,8)\n[postfix text](file:///jsdocLink2.ts#1,7-1,8)\nunformatted postfix text\n\n*@see* — [C](file:///jsdocLink2.ts#1,7-1,8) its great\n"
43+
},
44+
"range": {
45+
"start": {
46+
"line": 8,
47+
"character": 9
48+
},
49+
"end": {
50+
"line": 8,
51+
"character": 11
52+
}
53+
}
54+
}
55+
}
56+
]

0 commit comments

Comments
 (0)