From 890676a5d8f9f5c76057ab4556bf7cc7e8ebf2df Mon Sep 17 00:00:00 2001 From: Andy Hanson Date: Wed, 11 Jan 2017 14:28:22 -0800 Subject: [PATCH] Include properties of an `export =` value in import completions. --- src/compiler/checker.ts | 1 + src/compiler/types.ts | 1 + src/harness/fourslash.ts | 28 +++++++++++++++++-- src/services/completions.ts | 6 ++++ .../completionListForExportEquals.ts | 16 +++++++++++ .../completionListForExportEquals2.ts | 14 ++++++++++ .../completionListInImportClause04.ts | 5 +--- tests/cases/fourslash/fourslash.ts | 1 + 8 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 tests/cases/fourslash/completionListForExportEquals.ts create mode 100644 tests/cases/fourslash/completionListForExportEquals2.ts 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;