From 276e7989aa414a7b6fdc50df21fe10fb15eb1d88 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 14:47:39 -0700 Subject: [PATCH 1/8] Use a more accurate test name. --- ...{completionForExports.ts => completionListInImportClause01.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/cases/fourslash/{completionForExports.ts => completionListInImportClause01.ts} (100%) diff --git a/tests/cases/fourslash/completionForExports.ts b/tests/cases/fourslash/completionListInImportClause01.ts similarity index 100% rename from tests/cases/fourslash/completionForExports.ts rename to tests/cases/fourslash/completionListInImportClause01.ts From 03444a4d349c1d1b3b46cffa55ea096f479c1324 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 15:02:18 -0700 Subject: [PATCH 2/8] Verify builders. --- .../completionListInImportClause01.ts | 25 ++++++++++++------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/tests/cases/fourslash/completionListInImportClause01.ts b/tests/cases/fourslash/completionListInImportClause01.ts index 57c0f75efc1..cf053868dd6 100644 --- a/tests/cases/fourslash/completionListInImportClause01.ts +++ b/tests/cases/fourslash/completionListInImportClause01.ts @@ -11,22 +11,29 @@ ////import {foo,/*4*/ from "m1" ////import {bar as /*5*/, /*6*/ from "m1" ////import {foo, bar, baz as b,/*7*/} from "m1" -function verifyCompletionAtMarker(marker: string, ...completions: string[]) { +function verifyCompletionAtMarker(marker: string, showBuilder: boolean, ...completions: string[]) { goTo.marker(marker); if (completions.length) { - for (var i = 0; i < completions.length; ++i) { + for (let i = 0; i < completions.length; ++i) { verify.completionListContains(completions[i]); } } else { verify.completionListIsEmpty(); } + + if (showBuilder) { + verify.completionListAllowsNewIdentifier(); + } + else { + verify.not.completionListAllowsNewIdentifier(); + } } -verifyCompletionAtMarker("1", "foo", "bar", "baz"); -verifyCompletionAtMarker("2", "foo", "bar", "baz"); -verifyCompletionAtMarker("3", "foo", "bar", "baz"); -verifyCompletionAtMarker("4", "bar", "baz"); -verifyCompletionAtMarker("5"); -verifyCompletionAtMarker("6", "foo", "baz"); -verifyCompletionAtMarker("7"); \ No newline at end of file +verifyCompletionAtMarker("1", /*showBuilder*/ false, "foo", "bar", "baz"); +verifyCompletionAtMarker("2", /*showBuilder*/ false, "foo", "bar", "baz"); +verifyCompletionAtMarker("3", /*showBuilder*/ false, "foo", "bar", "baz"); +verifyCompletionAtMarker("4", /*showBuilder*/ false, "bar", "baz"); +verifyCompletionAtMarker("5", /*showBuilder*/ true); +verifyCompletionAtMarker("6", /*showBuilder*/ false, "foo", "baz"); +verifyCompletionAtMarker("7", /*showBuilder*/ false); \ No newline at end of file From 52581678d4f8341d0c83c2ae6991fab82edc75b6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 15:05:47 -0700 Subject: [PATCH 3/8] Refactor completion code for object literals/bindings and import clauses. --- src/services/services.ts | 114 ++++++++++++++++++++++----------------- 1 file changed, 64 insertions(+), 50 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index e401182fb11..6f7d233244e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3010,59 +3010,21 @@ namespace ts { } function tryGetGlobalSymbols(): boolean { - let objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken); - let jsxContainer = tryGetContainingJsxElement(contextToken); - if (objectLikeContainer) { - // Object literal expression, look up possible property names from contextual type - isMemberCompletion = true; - isNewIdentifierLocation = true; + let objectLikeContainer: ObjectLiteralExpression | BindingPattern; + let importClause: ImportClause; + let jsxContainer: JsxOpeningLikeElement; - let typeForObject: Type; - let existingMembers: Declaration[]; - - if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { - typeForObject = typeChecker.getContextualType(objectLikeContainer); - existingMembers = (objectLikeContainer).properties; - } - else { - typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); - existingMembers = (objectLikeContainer).elements; - } - - if (!typeForObject) { - return false; - } - - let typeMembers = typeChecker.getPropertiesOfType(typeForObject); - if (typeMembers && typeMembers.length > 0) { - // Add filtered items to the completion list - symbols = filterObjectMembersList(typeMembers, existingMembers); - } - return true; + if (objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken)) { + return tryGetObjectLikeCompletionSymbols(objectLikeContainer); } - else if (getAncestor(contextToken, SyntaxKind.ImportClause)) { - // cursor is in import clause + + if (importClause = getAncestor(contextToken, SyntaxKind.ImportClause)) { + // cursor is in an import clause // try to show exported member for imported module - isMemberCompletion = true; - isNewIdentifierLocation = true; - if (showCompletionsInImportsClause(contextToken)) { - let importDeclaration = getAncestor(contextToken, SyntaxKind.ImportDeclaration); - Debug.assert(importDeclaration !== undefined); - - let exports: Symbol[]; - if (importDeclaration.moduleSpecifier) { - let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importDeclaration.moduleSpecifier); - if (moduleSpecifierSymbol) { - exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); - } - } - - //let exports = typeInfoResolver.getExportsOfImportDeclaration(importDeclaration); - symbols = exports ? filterModuleExports(exports, importDeclaration) : emptyArray; - } - return true; + return tryGetImportClauseCompletionSymbols(importClause); } - else if (jsxContainer) { + + if (jsxContainer = tryGetContainingJsxElement(contextToken)) { let attrsType: Type; if ((jsxContainer.kind === SyntaxKind.JsxSelfClosingElement) || (jsxContainer.kind === SyntaxKind.JsxOpeningElement)) { // Cursor is inside a JSX self-closing element or opening element @@ -3144,7 +3106,7 @@ namespace ts { return result; } - function showCompletionsInImportsClause(node: Node): boolean { + function shouldShowCompletionsInImportsClause(node: Node): boolean { if (node) { // import {| // import {a,| @@ -3242,6 +3204,58 @@ namespace ts { return false; } + function tryGetObjectLikeCompletionSymbols(objectLikeContainer: ObjectLiteralExpression | BindingPattern): boolean { + // Object literal expression, look up possible property names from contextual type + isMemberCompletion = true; + isNewIdentifierLocation = true; + + let typeForObject: Type; + let existingMembers: Declaration[]; + + if (objectLikeContainer.kind === SyntaxKind.ObjectLiteralExpression) { + typeForObject = typeChecker.getContextualType(objectLikeContainer); + existingMembers = (objectLikeContainer).properties; + } + else { + typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer); + existingMembers = (objectLikeContainer).elements; + } + + if (!typeForObject) { + return false; + } + + let typeMembers = typeChecker.getPropertiesOfType(typeForObject); + if (typeMembers && typeMembers.length > 0) { + // Add filtered items to the completion list + symbols = filterObjectMembersList(typeMembers, existingMembers); + } + return true; + } + + function tryGetImportClauseCompletionSymbols(importClause: ImportClause): boolean { + // cursor is in import clause + // try to show exported member for imported module + isMemberCompletion = true; + isNewIdentifierLocation = true; + if (shouldShowCompletionsInImportsClause(contextToken)) { + let importDeclaration = getAncestor(contextToken, SyntaxKind.ImportDeclaration); + Debug.assert(importDeclaration !== undefined); + + let exports: Symbol[]; + if (importDeclaration.moduleSpecifier) { + let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importDeclaration.moduleSpecifier); + if (moduleSpecifierSymbol) { + exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); + } + } + + //let exports = typeInfoResolver.getExportsOfImportDeclaration(importDeclaration); + symbols = exports ? filterModuleExports(exports, importDeclaration) : emptyArray; + } + return true; + } + /** * Returns the immediate owning object literal or binding pattern of a context token, * on the condition that one exists and that the context implies completion should be given. From 4906f41f94cc7112a675699fc77c9b8ff085460a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 15:12:16 -0700 Subject: [PATCH 4/8] Remove builder from import clauses. --- src/services/services.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 6f7d233244e..5f2d1714482 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3236,9 +3236,10 @@ namespace ts { function tryGetImportClauseCompletionSymbols(importClause: ImportClause): boolean { // cursor is in import clause // try to show exported member for imported module - isMemberCompletion = true; - isNewIdentifierLocation = true; if (shouldShowCompletionsInImportsClause(contextToken)) { + isMemberCompletion = true; + isNewIdentifierLocation = false; + let importDeclaration = getAncestor(contextToken, SyntaxKind.ImportDeclaration); Debug.assert(importDeclaration !== undefined); @@ -3253,6 +3254,11 @@ namespace ts { //let exports = typeInfoResolver.getExportsOfImportDeclaration(importDeclaration); symbols = exports ? filterModuleExports(exports, importDeclaration) : emptyArray; } + else { + isMemberCompletion = false; + isNewIdentifierLocation = true; + } + return true; } From 40a6300e02de76eebc4ab35ae4c113dd23185555 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 15:15:04 -0700 Subject: [PATCH 5/8] Added original test case. --- .../completionListInImportClause02.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/cases/fourslash/completionListInImportClause02.ts diff --git a/tests/cases/fourslash/completionListInImportClause02.ts b/tests/cases/fourslash/completionListInImportClause02.ts new file mode 100644 index 00000000000..570dd452a7f --- /dev/null +++ b/tests/cases/fourslash/completionListInImportClause02.ts @@ -0,0 +1,18 @@ +/// + +// @Filename: m1.ts +////export var foo: number = 1; +////export function bar() { return 10; } +////export function baz() { return 10; } + +////declare module "M1" { +//// export var V; +////} +//// +////declare module "M2" { +//// import { /**/ } from "M1" +////} + +goTo.marker(); +verify.completionListContains("V"); +verify.not.completionListAllowsNewIdentifier(); From 97dd855663e48ee6d84e99869fc70c543dba5193 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 15:33:49 -0700 Subject: [PATCH 6/8] Fix test. --- tests/cases/fourslash/completionListInImportClause02.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/cases/fourslash/completionListInImportClause02.ts b/tests/cases/fourslash/completionListInImportClause02.ts index 570dd452a7f..9cf216f4578 100644 --- a/tests/cases/fourslash/completionListInImportClause02.ts +++ b/tests/cases/fourslash/completionListInImportClause02.ts @@ -1,10 +1,5 @@ /// -// @Filename: m1.ts -////export var foo: number = 1; -////export function bar() { return 10; } -////export function baz() { return 10; } - ////declare module "M1" { //// export var V; ////} From be183819269eb691f393b5fc0fbe668602e8e698 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Mon, 6 Jul 2015 15:34:44 -0700 Subject: [PATCH 7/8] Removed unnecessary check. --- src/services/services.ts | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 5f2d1714482..d2f9ca8b890 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3240,15 +3240,13 @@ namespace ts { isMemberCompletion = true; isNewIdentifierLocation = false; - let importDeclaration = getAncestor(contextToken, SyntaxKind.ImportDeclaration); - Debug.assert(importDeclaration !== undefined); + let importDeclaration = importClause.parent; + Debug.assert(importDeclaration !== undefined && importDeclaration.kind === SyntaxKind.ImportDeclaration); let exports: Symbol[]; - if (importDeclaration.moduleSpecifier) { - let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importDeclaration.moduleSpecifier); - if (moduleSpecifierSymbol) { - exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); - } + let moduleSpecifierSymbol = typeChecker.getSymbolAtLocation(importDeclaration.moduleSpecifier); + if (moduleSpecifierSymbol) { + exports = typeChecker.getExportsOfModule(moduleSpecifierSymbol); } //let exports = typeInfoResolver.getExportsOfImportDeclaration(importDeclaration); From d353d624346d9372bf74792acacdf5d8cffb7f71 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Tue, 7 Jul 2015 12:48:17 -0700 Subject: [PATCH 8/8] Addressed CR feedback. --- src/services/services.ts | 15 +++++++++++++++ .../fourslash/completionListInImportClause01.ts | 4 ++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/services/services.ts b/src/services/services.ts index 4149a7e786c..28c0b0ce239 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3204,6 +3204,12 @@ namespace ts { return false; } + /** + * Aggregates relevant symbols for completion in object literals and object binding patterns. + * Relevant symbols are stored in the captured 'symbols' variable. + * + * @returns true if 'symbols' was successfully populated; false otherwise. + */ function tryGetObjectLikeCompletionSymbols(objectLikeContainer: ObjectLiteralExpression | BindingPattern): boolean { // We're looking up possible property names from contextual/inferred/declared type. isMemberCompletion = true; @@ -3242,6 +3248,15 @@ namespace ts { return true; } + /** + * Aggregates relevant symbols for completion in import clauses; for instance, + * + * import { $ } from "moduleName"; + * + * Relevant symbols are stored in the captured 'symbols' variable. + * + * @returns true if 'symbols' was successfully populated; false otherwise. + */ function tryGetImportClauseCompletionSymbols(importClause: ImportClause): boolean { // cursor is in import clause // try to show exported member for imported module diff --git a/tests/cases/fourslash/completionListInImportClause01.ts b/tests/cases/fourslash/completionListInImportClause01.ts index cf053868dd6..1191216baf1 100644 --- a/tests/cases/fourslash/completionListInImportClause01.ts +++ b/tests/cases/fourslash/completionListInImportClause01.ts @@ -14,8 +14,8 @@ function verifyCompletionAtMarker(marker: string, showBuilder: boolean, ...completions: string[]) { goTo.marker(marker); if (completions.length) { - for (let i = 0; i < completions.length; ++i) { - verify.completionListContains(completions[i]); + for (let completion of completions) { + verify.completionListContains(completion); } } else {