diff --git a/src/harness/fourslashInterfaceImpl.ts b/src/harness/fourslashInterfaceImpl.ts
index 0eb1071681a..08c1c788f63 100644
--- a/src/harness/fourslashInterfaceImpl.ts
+++ b/src/harness/fourslashInterfaceImpl.ts
@@ -23,6 +23,10 @@ namespace FourSlashInterface {
return this.state.getRanges();
}
+ public rangesInFile(fileName?: string): FourSlash.Range[] {
+ return this.state.getRangesInFile(fileName);
+ }
+
public spans(): ts.TextSpan[] {
return this.ranges().map(r => ts.createTextSpan(r.pos, r.end - r.pos));
}
diff --git a/src/services/utilities.ts b/src/services/utilities.ts
index 789ff81f861..0fc816729ff 100644
--- a/src/services/utilities.ts
+++ b/src/services/utilities.ts
@@ -89,21 +89,30 @@ namespace ts {
export function getMeaningFromLocation(node: Node): SemanticMeaning {
node = getAdjustedReferenceLocation(node);
+ const parent = node.parent;
if (node.kind === SyntaxKind.SourceFile) {
return SemanticMeaning.Value;
}
- else if (node.parent.kind === SyntaxKind.ExportAssignment
- || node.parent.kind === SyntaxKind.ExternalModuleReference
- || node.parent.kind === SyntaxKind.ImportSpecifier
- || node.parent.kind === SyntaxKind.ImportClause
- || isImportEqualsDeclaration(node.parent) && node === node.parent.name) {
+ else if (isExportAssignment(parent)
+ || isExportSpecifier(parent)
+ || isExternalModuleReference(parent)
+ || isImportSpecifier(parent)
+ || isImportClause(parent)
+ || isImportEqualsDeclaration(parent) && node === parent.name) {
+ let decl: Node = parent;
+ while (decl) {
+ if (isImportEqualsDeclaration(decl) || isImportClause(decl) || isExportDeclaration(decl)) {
+ return decl.isTypeOnly ? SemanticMeaning.Type : SemanticMeaning.All;
+ }
+ decl = decl.parent;
+ }
return SemanticMeaning.All;
}
else if (isInRightSideOfInternalImportEqualsDeclaration(node)) {
return getMeaningFromRightHandSideOfImportEquals(node as Identifier);
}
else if (isDeclarationName(node)) {
- return getMeaningFromDeclaration(node.parent);
+ return getMeaningFromDeclaration(parent);
}
else if (isEntityName(node) && findAncestor(node, or(isJSDocNameReference, isJSDocLinkLike, isJSDocMemberName))) {
return SemanticMeaning.All;
@@ -114,11 +123,11 @@ namespace ts {
else if (isNamespaceReference(node)) {
return SemanticMeaning.Namespace;
}
- else if (isTypeParameterDeclaration(node.parent)) {
- Debug.assert(isJSDocTemplateTag(node.parent.parent)); // Else would be handled by isDeclarationName
+ else if (isTypeParameterDeclaration(parent)) {
+ Debug.assert(isJSDocTemplateTag(parent.parent)); // Else would be handled by isDeclarationName
return SemanticMeaning.Type;
}
- else if (isLiteralTypeNode(node.parent)) {
+ else if (isLiteralTypeNode(parent)) {
// This might be T["name"], which is actually referencing a property and not a type. So allow both meanings.
return SemanticMeaning.Type | SemanticMeaning.Value;
}
diff --git a/tests/cases/fourslash/documentHighlightInTypeExport.ts b/tests/cases/fourslash/documentHighlightInTypeExport.ts
new file mode 100644
index 00000000000..5a16a9bbc90
--- /dev/null
+++ b/tests/cases/fourslash/documentHighlightInTypeExport.ts
@@ -0,0 +1,55 @@
+///
+
+// @Filename: /1.ts
+//// type [|A|] = 1;
+//// export { [|A|] as [|B|] };
+
+{
+ const [AType, AExport, asB] = test.rangesInFile("/1.ts");
+ verify.documentHighlightsOf(AType, [AType, AExport, asB]);
+ verify.documentHighlightsOf(AExport, [AType, AExport, asB]);
+ verify.documentHighlightsOf(asB, [asB]);
+}
+
+// @Filename: /2.ts
+//// type [|A|] = 1;
+//// let [|A|]: [|A|] = 1;
+//// export { [|A|] as [|B|] };
+
+{ // a little strange, but the the type/value namespaces work too
+ const [AType, ALet, ADecl, AExport, asB] = test.rangesInFile("/2.ts");
+ verify.documentHighlightsOf(AType, [AType, ADecl, AExport, asB]);
+ verify.documentHighlightsOf(ADecl, [AType, ADecl, AExport, asB]);
+ verify.documentHighlightsOf(ALet, [ALet, AExport, asB]);
+ verify.documentHighlightsOf(AExport, [AType, ALet, ADecl, AExport, asB]);
+ verify.documentHighlightsOf(asB, [asB]);
+}
+
+// @Filename: /3.ts
+//// type [|A|] = 1;
+//// let [|A|]: [|A|] = 1;
+//// export type { [|A|] as [|B|] };
+
+{ // properly handle type only
+ const [AType, ALet, ADecl, AExport, asB] = test.rangesInFile("/3.ts");
+ verify.documentHighlightsOf(AType, [AType, ADecl, AExport, asB]);
+ verify.documentHighlightsOf(ADecl, [AType, ADecl, AExport, asB]);
+ verify.documentHighlightsOf(AExport, [AType, ADecl, AExport, asB]);
+ verify.documentHighlightsOf(ALet, [ALet]);
+ verify.documentHighlightsOf(asB, [asB]);
+}
+
+// would be nice if this could work the same for imports too, but getSymbolAtLocation()
+// of the imported symbol (when aliased) returns undefined
+
+// // @Filename: /4.ts
+// //// import type { [|Tee|] as [|T|] } from "whatEveh";
+// //// let [|T|]: [|T|];
+//
+// {
+// const [TeeImport, asT, TLet, TDecl] = test.rangesInFile("/4.ts");
+// verify.documentHighlightsOf(TeeImport, [TeeImport, asT, TDecl]);
+// // verify.documentHighlightsOf(asT, [TeeImport, asT, TDecl]);
+// // verify.documentHighlightsOf(TDecl, [TeeImport, asT, TDecl]);
+// // verify.documentHighlightsOf(TLet, [TLet]);
+// }
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 26c3c220b72..2b4a197a3cb 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -187,6 +187,7 @@ declare namespace FourSlashInterface {
markerName(m: Marker): string;
marker(name?: string): Marker;
ranges(): Range[];
+ rangesInFile(fileName?: string): Range[];
spans(): Array<{ start: number, length: number }>;
rangesByText(): ts.Map;
markerByName(s: string): Marker;