diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index cc4afc8aa3e..46a381a561e 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -108,6 +108,7 @@ namespace ts {
getAliasedSymbol: resolveAlias,
getEmitResolver,
getExportsOfModule: getExportsOfModuleAsArray,
+ resolveExternalModuleSymbol,
getAmbientModules,
getJsxElementAttributesType,
getJsxIntrinsicTagNames,
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 954ad21ba29..8b3b821a25b 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -2370,6 +2370,7 @@ namespace ts {
isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean;
getAliasedSymbol(symbol: Symbol): Symbol;
getExportsOfModule(moduleSymbol: Symbol): Symbol[];
+ /* @internal */ resolveExternalModuleSymbol(moduleSymbol: Symbol): Symbol;
getJsxElementAttributesType(elementNode: JsxOpeningLikeElement): Type;
getJsxIntrinsicTagNames(): Symbol[];
diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts
index c41c9af9f4e..f0f2f87175e 100644
--- a/src/harness/fourslash.ts
+++ b/src/harness/fourslash.ts
@@ -425,8 +425,7 @@ namespace FourSlash {
}
private raiseError(message: string) {
- message = this.messageAtLastKnownMarker(message);
- throw new Error(message);
+ throw new Error(this.messageAtLastKnownMarker(message));
}
private messageAtLastKnownMarker(message: string) {
@@ -723,6 +722,27 @@ namespace FourSlash {
}
}
+ public verifyCompletionsAt(markerName: string, expected: string[]) {
+ this.goToMarker(markerName);
+
+ const actualCompletions = this.getCompletionListAtCaret();
+ if (!actualCompletions) {
+ this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
+ }
+
+ const actual = actualCompletions.entries;
+
+ if (actual.length !== expected.length) {
+ this.raiseError(`Expected ${expected.length} completions, got ${actual.map(a => a.name)}.`);
+ }
+
+ ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
+ if (completion.name !== expectedCompletion) {
+ this.raiseError(`Expected completion at index ${index} to be ${expectedCompletion}, got ${completion.name}`);
+ }
+ });
+ }
+
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
const completions = this.getCompletionListAtCaret();
if (completions) {
@@ -3166,6 +3186,10 @@ namespace FourSlashInterface {
super(state);
}
+ public completionsAt(markerName: string, completions: string[]) {
+ this.state.verifyCompletionsAt(markerName, completions);
+ }
+
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
}
diff --git a/src/services/completions.ts b/src/services/completions.ts
index d893e7212ec..27cd78bbe17 100644
--- a/src/services/completions.ts
+++ b/src/services/completions.ts
@@ -1322,6 +1322,12 @@ namespace ts.Completions {
const moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importOrExportDeclaration.moduleSpecifier);
if (moduleSpecifierSymbol) {
exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol);
+ const exportEquals = typeChecker.resolveExternalModuleSymbol(moduleSpecifierSymbol);
+ if (exportEquals !== moduleSpecifierSymbol) {
+ // Location doesn't matter so long as it's not an identifier.
+ const exportEqualsType = typeChecker.getTypeOfSymbolAtLocation(exportEquals, moduleSpecifier);
+ exports = ts.concatenate(exports, typeChecker.getPropertiesOfType(exportEqualsType));
+ }
}
symbols = exports ? filterNamedImportOrExportCompletionItems(exports, namedImportsOrExports.elements) : emptyArray;
diff --git a/tests/cases/fourslash/completionListForExportEquals.ts b/tests/cases/fourslash/completionListForExportEquals.ts
new file mode 100644
index 00000000000..28b20177c57
--- /dev/null
+++ b/tests/cases/fourslash/completionListForExportEquals.ts
@@ -0,0 +1,16 @@
+
+///
+
+// @Filename: /node_modules/foo/index.d.ts
+////export = Foo;
+////declare var Foo: Foo.Static;
+////declare namespace Foo {
+//// interface Static {
+//// foo(): void;
+//// }
+////}
+
+// @Filename: /a.ts
+////import { /**/ } from "foo";
+
+verify.completionsAt("", ["Static", "foo"]);
diff --git a/tests/cases/fourslash/completionListForExportEquals2.ts b/tests/cases/fourslash/completionListForExportEquals2.ts
new file mode 100644
index 00000000000..a7b0772d55e
--- /dev/null
+++ b/tests/cases/fourslash/completionListForExportEquals2.ts
@@ -0,0 +1,14 @@
+
+///
+
+// @Filename: /node_modules/foo/index.d.ts
+////export = Foo;
+////interface Foo { bar: number; }
+////declare namespace Foo {
+//// interface Static {}
+////}
+
+// @Filename: /a.ts
+////import { /**/ } from "foo";
+
+verify.completionsAt("", ["Static"]);
diff --git a/tests/cases/fourslash/completionListInImportClause04.ts b/tests/cases/fourslash/completionListInImportClause04.ts
index 672c754e807..fa49b57c8d7 100644
--- a/tests/cases/fourslash/completionListInImportClause04.ts
+++ b/tests/cases/fourslash/completionListInImportClause04.ts
@@ -11,10 +11,7 @@
// @Filename: app.ts
////import {/*1*/} from './foo';
-goTo.marker('1');
-verify.completionListContains('prop1');
-verify.completionListContains('prop2');
-verify.not.completionListContains('Foo');
+verify.completionsAt("1", ["prototype", "prop1", "prop2"]);
verify.numberOfErrorsInCurrentFile(0);
goTo.marker('2');
verify.numberOfErrorsInCurrentFile(0);
diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts
index 4397839fbe8..0de303238f1 100644
--- a/tests/cases/fourslash/fourslash.ts
+++ b/tests/cases/fourslash/fourslash.ts
@@ -138,6 +138,7 @@ declare namespace FourSlashInterface {
class verify extends verifyNegatable {
assertHasRanges(ranges: Range[]): void;
caretAtMarker(markerName?: string): void;
+ completionsAt(markerName: string, completions: string[]): void;
indentationIs(numberOfSpaces: number): void;
indentationAtPositionIs(fileName: string, position: number, numberOfSpaces: number, indentStyle?: ts.IndentStyle, baseIndentSize?: number): void;
textAtCaretIs(text: string): void;