From 3b1b5e241f69bfcf49309094359e2620ada6cd06 Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 31 Jan 2017 16:35:30 -0800 Subject: [PATCH 1/3] Add string literal completion for equality expression --- src/services/completions.ts | 24 +++++++++++++++++++ .../fourslash/completionForStringLiteral10.ts | 12 ++++++++++ .../fourslash/completionForStringLiteral8.ts | 12 ++++++++++ 3 files changed, 48 insertions(+) create mode 100644 tests/cases/fourslash/completionForStringLiteral10.ts create mode 100644 tests/cases/fourslash/completionForStringLiteral8.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 5a08dbcb9c8..5d70b357625 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -173,6 +173,11 @@ namespace ts.Completions { // var y = require("/*completion position*/"); return getStringLiteralCompletionEntriesFromModuleNames(node, compilerOptions, host, typeChecker); } + else if (isEqualityExpression(node.parent)) { + // Get all known external module names or complete a path to a module + // i.e. x === '/*completion position' + return getStringLiteralCompletionEntriesFromBinaryExpression(node, node.parent, typeChecker); + } else { const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile); if (argumentInfo) { @@ -240,6 +245,18 @@ namespace ts.Completions { return undefined; } + function getStringLiteralCompletionEntriesFromBinaryExpression(node: StringLiteral, parent: BinaryExpression, typeChecker: TypeChecker): CompletionInfo | undefined { + const type = typeChecker.getTypeAtLocation(parent.left === node ? parent.right : parent.left); + if (type) { + const entries: CompletionEntry[] = []; + addStringLiteralCompletionsFromType(type, entries, typeChecker); + if (entries.length) { + return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; + } + } + return undefined; + } + function addStringLiteralCompletionsFromType(type: Type, result: Push, typeChecker: TypeChecker): void { if (type && type.flags & TypeFlags.TypeParameter) { type = typeChecker.getApparentType(type); @@ -1756,4 +1773,11 @@ namespace ts.Completions { catch (e) {} return undefined; } + + function isEqualityExpression(node: Node): node is BinaryExpression { + return isBinaryExpression(node) && (node.operatorToken.kind == SyntaxKind.EqualsEqualsToken || + node.operatorToken.kind === SyntaxKind.ExclamationEqualsToken || + node.operatorToken.kind === SyntaxKind.EqualsEqualsEqualsToken || + node.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken); + } } diff --git a/tests/cases/fourslash/completionForStringLiteral10.ts b/tests/cases/fourslash/completionForStringLiteral10.ts new file mode 100644 index 00000000000..76a98eb732f --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral10.ts @@ -0,0 +1,12 @@ +/// + +////type As = 'arf' | 'abacus' | 'abaddon'; +////let a: As; +////if ('/**/' != a + +goTo.marker(); +verify.completionListContains("arf"); +verify.completionListContains("abacus"); +verify.completionListContains("abaddon"); +verify.completionListCount(3); + diff --git a/tests/cases/fourslash/completionForStringLiteral8.ts b/tests/cases/fourslash/completionForStringLiteral8.ts new file mode 100644 index 00000000000..727bdf6e6a4 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral8.ts @@ -0,0 +1,12 @@ +/// + +////type As = 'arf' | 'abacus' | 'abaddon'; +////let a: As; +////if (a === '/**/ + +goTo.marker(); +verify.completionListContains("arf"); +verify.completionListContains("abacus"); +verify.completionListContains("abaddon"); +verify.completionListCount(3); + From 52841924dad0909e1284352d1ab9b14c00437fda Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 31 Jan 2017 16:35:30 -0800 Subject: [PATCH 2/3] Support string literal completion in case labels --- src/services/completions.ts | 28 ++++++++----------- .../fourslash/completionForStringLiteral11.ts | 14 ++++++++++ 2 files changed, 25 insertions(+), 17 deletions(-) create mode 100644 tests/cases/fourslash/completionForStringLiteral11.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 5d70b357625..4c882c9b6b6 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -174,9 +174,16 @@ namespace ts.Completions { return getStringLiteralCompletionEntriesFromModuleNames(node, compilerOptions, host, typeChecker); } else if (isEqualityExpression(node.parent)) { - // Get all known external module names or complete a path to a module + // Get completions from the type of the other operand + // i.e. switch (a) { + // case '/*completion position*/' + // } + return getStringLiteralCompletionEntriesFromType(typeChecker.getTypeAtLocation(node.parent.left === node ? node.parent.right : node.parent.left), typeChecker); + } + else if (isCaseOrDefaultClause(node.parent)) { + // Get completions from the type of the switch expression // i.e. x === '/*completion position' - return getStringLiteralCompletionEntriesFromBinaryExpression(node, node.parent, typeChecker); + return getStringLiteralCompletionEntriesFromType(typeChecker.getTypeAtLocation((node.parent.parent.parent).expression), typeChecker); } else { const argumentInfo = SignatureHelp.getImmediatelyContainingArgumentInfo(node, position, sourceFile); @@ -189,7 +196,7 @@ namespace ts.Completions { // Get completion for string literal from string literal type // i.e. var x: "hi" | "hello" = "/*completion position*/" - return getStringLiteralCompletionEntriesFromContextualType(node, typeChecker); + return getStringLiteralCompletionEntriesFromType(typeChecker.getContextualType(node), typeChecker); } } @@ -233,20 +240,7 @@ namespace ts.Completions { return undefined; } - function getStringLiteralCompletionEntriesFromContextualType(node: StringLiteral, typeChecker: TypeChecker): CompletionInfo | undefined { - const type = typeChecker.getContextualType(node); - if (type) { - const entries: CompletionEntry[] = []; - addStringLiteralCompletionsFromType(type, entries, typeChecker); - if (entries.length) { - return { isGlobalCompletion: false, isMemberCompletion: false, isNewIdentifierLocation: false, entries }; - } - } - return undefined; - } - - function getStringLiteralCompletionEntriesFromBinaryExpression(node: StringLiteral, parent: BinaryExpression, typeChecker: TypeChecker): CompletionInfo | undefined { - const type = typeChecker.getTypeAtLocation(parent.left === node ? parent.right : parent.left); + function getStringLiteralCompletionEntriesFromType(type: Type, typeChecker: TypeChecker): CompletionInfo | undefined { if (type) { const entries: CompletionEntry[] = []; addStringLiteralCompletionsFromType(type, entries, typeChecker); diff --git a/tests/cases/fourslash/completionForStringLiteral11.ts b/tests/cases/fourslash/completionForStringLiteral11.ts new file mode 100644 index 00000000000..f6acac28a65 --- /dev/null +++ b/tests/cases/fourslash/completionForStringLiteral11.ts @@ -0,0 +1,14 @@ +/// + +////type As = 'arf' | 'abacus' | 'abaddon'; +////let a: As; +////switch (a) { +//// case '/**/ +////} + +goTo.marker(); +verify.completionListContains("arf"); +verify.completionListContains("abacus"); +verify.completionListContains("abaddon"); +verify.completionListCount(3); + From a39c14ec2e85bd61cd9c1820232e6840a5ffe07d Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Tue, 31 Jan 2017 21:34:25 -0800 Subject: [PATCH 3/3] Factor kind check to a separate function --- src/services/completions.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 4c882c9b6b6..df78ab931d1 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -1769,9 +1769,13 @@ namespace ts.Completions { } function isEqualityExpression(node: Node): node is BinaryExpression { - return isBinaryExpression(node) && (node.operatorToken.kind == SyntaxKind.EqualsEqualsToken || - node.operatorToken.kind === SyntaxKind.ExclamationEqualsToken || - node.operatorToken.kind === SyntaxKind.EqualsEqualsEqualsToken || - node.operatorToken.kind === SyntaxKind.ExclamationEqualsEqualsToken); + return isBinaryExpression(node) && isEqualityOperatorKind(node.operatorToken.kind); + } + + function isEqualityOperatorKind(kind: SyntaxKind) { + return kind == SyntaxKind.EqualsEqualsToken || + kind === SyntaxKind.ExclamationEqualsToken || + kind === SyntaxKind.EqualsEqualsEqualsToken || + kind === SyntaxKind.ExclamationEqualsEqualsToken; } }