Skip to content

Commit fad9122

Browse files
committed
services/utilities/getMeaningFromLocation(): fix w/ export specifiers
Fixes 44167, but also two other things: * On an import/export, climb upto the declaration, and use `SemanticMeaning.Type` if it's a `type` only import/export. * Add a `test.rangesInFile()` to fourslash, so it is easy to do multiple files in one test without an awkward filter (which was done in one more test).
1 parent eee34d5 commit fad9122

File tree

4 files changed

+78
-9
lines changed

4 files changed

+78
-9
lines changed

src/harness/fourslashInterfaceImpl.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ namespace FourSlashInterface {
2323
return this.state.getRanges();
2424
}
2525

26+
public rangesInFile(fileName?: string): FourSlash.Range[] {
27+
return this.state.getRangesInFile(fileName);
28+
}
29+
2630
public spans(): ts.TextSpan[] {
2731
return this.ranges().map(r => ts.createTextSpan(r.pos, r.end - r.pos));
2832
}

src/services/utilities.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -89,21 +89,30 @@ namespace ts {
8989

9090
export function getMeaningFromLocation(node: Node): SemanticMeaning {
9191
node = getAdjustedReferenceLocation(node);
92+
const parent = node.parent;
9293
if (node.kind === SyntaxKind.SourceFile) {
9394
return SemanticMeaning.Value;
9495
}
95-
else if (node.parent.kind === SyntaxKind.ExportAssignment
96-
|| node.parent.kind === SyntaxKind.ExternalModuleReference
97-
|| node.parent.kind === SyntaxKind.ImportSpecifier
98-
|| node.parent.kind === SyntaxKind.ImportClause
99-
|| isImportEqualsDeclaration(node.parent) && node === node.parent.name) {
96+
else if (isExportAssignment(parent)
97+
|| isExportSpecifier(parent)
98+
|| isExternalModuleReference(parent)
99+
|| isImportSpecifier(parent)
100+
|| isImportClause(parent)
101+
|| isImportEqualsDeclaration(parent) && node === parent.name) {
102+
let decl: Node = parent;
103+
while (decl) {
104+
if (isImportEqualsDeclaration(decl) || isImportClause(decl) || isExportDeclaration(decl)) {
105+
return decl.isTypeOnly ? SemanticMeaning.Type : SemanticMeaning.All;
106+
}
107+
decl = decl.parent;
108+
}
100109
return SemanticMeaning.All;
101110
}
102111
else if (isInRightSideOfInternalImportEqualsDeclaration(node)) {
103112
return getMeaningFromRightHandSideOfImportEquals(node as Identifier);
104113
}
105114
else if (isDeclarationName(node)) {
106-
return getMeaningFromDeclaration(node.parent);
115+
return getMeaningFromDeclaration(parent);
107116
}
108117
else if (isEntityName(node) && findAncestor(node, or(isJSDocNameReference, isJSDocLinkLike, isJSDocMemberName))) {
109118
return SemanticMeaning.All;
@@ -114,11 +123,11 @@ namespace ts {
114123
else if (isNamespaceReference(node)) {
115124
return SemanticMeaning.Namespace;
116125
}
117-
else if (isTypeParameterDeclaration(node.parent)) {
118-
Debug.assert(isJSDocTemplateTag(node.parent.parent)); // Else would be handled by isDeclarationName
126+
else if (isTypeParameterDeclaration(parent)) {
127+
Debug.assert(isJSDocTemplateTag(parent.parent)); // Else would be handled by isDeclarationName
119128
return SemanticMeaning.Type;
120129
}
121-
else if (isLiteralTypeNode(node.parent)) {
130+
else if (isLiteralTypeNode(parent)) {
122131
// This might be T["name"], which is actually referencing a property and not a type. So allow both meanings.
123132
return SemanticMeaning.Type | SemanticMeaning.Value;
124133
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/// <reference path='fourslash.ts'/>
2+
3+
// @Filename: /1.ts
4+
//// type [|A|] = 1;
5+
//// export { [|A|] as [|B|] };
6+
7+
{
8+
const [AType, AExport, asB] = test.rangesInFile("/1.ts");
9+
verify.documentHighlightsOf(AType, [AType, AExport, asB]);
10+
verify.documentHighlightsOf(AExport, [AType, AExport, asB]);
11+
verify.documentHighlightsOf(asB, [asB]);
12+
}
13+
14+
// @Filename: /2.ts
15+
//// type [|A|] = 1;
16+
//// let [|A|]: [|A|] = 1;
17+
//// export { [|A|] as [|B|] };
18+
19+
{ // a little strange, but the the type/value namespaces work too
20+
const [AType, ALet, ADecl, AExport, asB] = test.rangesInFile("/2.ts");
21+
verify.documentHighlightsOf(AType, [AType, ADecl, AExport, asB]);
22+
verify.documentHighlightsOf(ADecl, [AType, ADecl, AExport, asB]);
23+
verify.documentHighlightsOf(ALet, [ALet, AExport, asB]);
24+
verify.documentHighlightsOf(AExport, [AType, ALet, ADecl, AExport, asB]);
25+
verify.documentHighlightsOf(asB, [asB]);
26+
}
27+
28+
// @Filename: /3.ts
29+
//// type [|A|] = 1;
30+
//// let [|A|]: [|A|] = 1;
31+
//// export type { [|A|] as [|B|] };
32+
33+
{ // properly handle type only
34+
const [AType, ALet, ADecl, AExport, asB] = test.rangesInFile("/3.ts");
35+
verify.documentHighlightsOf(AType, [AType, ADecl, AExport, asB]);
36+
verify.documentHighlightsOf(ADecl, [AType, ADecl, AExport, asB]);
37+
verify.documentHighlightsOf(AExport, [AType, ADecl, AExport, asB]);
38+
verify.documentHighlightsOf(ALet, [ALet]);
39+
verify.documentHighlightsOf(asB, [asB]);
40+
}
41+
42+
// would be nice if this could work the same for imports too, but getSymbolAtLocation()
43+
// of the imported symbol (when aliased) returns undefined
44+
45+
// // @Filename: /4.ts
46+
// //// import type { [|Tee|] as [|T|] } from "whatEveh";
47+
// //// let [|T|]: [|T|];
48+
//
49+
// {
50+
// const [TeeImport, asT, TLet, TDecl] = test.rangesInFile("/4.ts");
51+
// verify.documentHighlightsOf(TeeImport, [TeeImport, asT, TDecl]);
52+
// // verify.documentHighlightsOf(asT, [TeeImport, asT, TDecl]);
53+
// // verify.documentHighlightsOf(TDecl, [TeeImport, asT, TDecl]);
54+
// // verify.documentHighlightsOf(TLet, [TLet]);
55+
// }

tests/cases/fourslash/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ declare namespace FourSlashInterface {
187187
markerName(m: Marker): string;
188188
marker(name?: string): Marker;
189189
ranges(): Range[];
190+
rangesInFile(fileName?: string): Range[];
190191
spans(): Array<{ start: number, length: number }>;
191192
rangesByText(): ts.Map<Range[]>;
192193
markerByName(s: string): Marker;

0 commit comments

Comments
 (0)