diff --git a/src/services/services.ts b/src/services/services.ts index 28c0b0ce239..d79436bb478 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2892,33 +2892,36 @@ namespace ts { log("getCompletionData: Get previous token 2: " + (new Date().getTime() - start)); } - // Check if this is a valid completion location - if (contextToken && isCompletionListBlocker(contextToken)) { - log("Returning an empty list because completion was requested in an invalid position."); - return undefined; - } - - let options = program.getCompilerOptions(); - let jsx = options.jsx !== JsxEmit.None; - let target = options.target; - - // Find the node where completion is requested on, in the case of a completion after - // a dot, it is the member access expression other wise, it is a request for all - // visible symbols in the scope, and the node is the current location. + // Find the node where completion is requested on. + // Also determine whether we are trying to complete with members of that node + // or attributes of a JSX tag. let node = currentToken; let isRightOfDot = false; let isRightOfOpenTag = false; let location = getTouchingPropertyName(sourceFile, position); - if(contextToken) { - let kind = contextToken.kind; - if (kind === SyntaxKind.DotToken && contextToken.parent.kind === SyntaxKind.PropertyAccessExpression) { - node = (contextToken.parent).expression; - isRightOfDot = true; + if (contextToken) { + // Bail out if this is a known invalid completion location + if (isCompletionListBlocker(contextToken)) { + log("Returning an empty list because completion was requested in an invalid position."); + return undefined; } - else if (kind === SyntaxKind.DotToken && contextToken.parent.kind === SyntaxKind.QualifiedName) { - node = (contextToken.parent).left; - isRightOfDot = true; + + let { parent, kind } = contextToken; + if (kind === SyntaxKind.DotToken) { + if (parent.kind === SyntaxKind.PropertyAccessExpression) { + node = (contextToken.parent).expression; + isRightOfDot = true; + } + else if (parent.kind === SyntaxKind.QualifiedName) { + node = (contextToken.parent).left; + isRightOfDot = true; + } + else { + // There is nothing that precedes the dot, so this likely just a stray character + // or leading into a '...' token. Just bail out instead. + return undefined; + } } else if (kind === SyntaxKind.LessThanToken && sourceFile.languageVariant === LanguageVariant.JSX) { isRightOfOpenTag = true; @@ -3097,11 +3100,11 @@ namespace ts { return scope; } - function isCompletionListBlocker(previousToken: Node): boolean { + function isCompletionListBlocker(contextToken: Node): boolean { let start = new Date().getTime(); - let result = isInStringOrRegularExpressionOrTemplateLiteral(previousToken) || - isIdentifierDefinitionLocation(previousToken) || - isRightOfIllegalDot(previousToken); + let result = isInStringOrRegularExpressionOrTemplateLiteral(contextToken) || + isIdentifierDefinitionLocation(contextToken) || + isDotOfNumericLiteral(contextToken); log("getCompletionsAtPosition: isCompletionListBlocker: " + (new Date().getTime() - start)); return result; } @@ -3180,12 +3183,12 @@ namespace ts { return false; } - function isInStringOrRegularExpressionOrTemplateLiteral(previousToken: Node): boolean { - if (previousToken.kind === SyntaxKind.StringLiteral - || previousToken.kind === SyntaxKind.RegularExpressionLiteral - || isTemplateLiteralKind(previousToken.kind)) { - let start = previousToken.getStart(); - let end = previousToken.getEnd(); + function isInStringOrRegularExpressionOrTemplateLiteral(contextToken: Node): boolean { + if (contextToken.kind === SyntaxKind.StringLiteral + || contextToken.kind === SyntaxKind.RegularExpressionLiteral + || isTemplateLiteralKind(contextToken.kind)) { + let start = contextToken.getStart(); + let end = contextToken.getEnd(); // To be "in" one of these literals, the position has to be: // 1. entirely within the token text. @@ -3196,8 +3199,8 @@ namespace ts { } if (position === end) { - return !!(previousToken).isUnterminated || - previousToken.kind === SyntaxKind.RegularExpressionLiteral; + return !!(contextToken).isUnterminated + || contextToken.kind === SyntaxKind.RegularExpressionLiteral; } } @@ -3351,101 +3354,98 @@ namespace ts { return false; } - function isIdentifierDefinitionLocation(previousToken: Node): boolean { - if (previousToken) { - let containingNodeKind = previousToken.parent.kind; - switch (previousToken.kind) { - case SyntaxKind.CommaToken: - return containingNodeKind === SyntaxKind.VariableDeclaration || - containingNodeKind === SyntaxKind.VariableDeclarationList || - containingNodeKind === SyntaxKind.VariableStatement || - containingNodeKind === SyntaxKind.EnumDeclaration || // enum a { foo, | - isFunction(containingNodeKind) || - containingNodeKind === SyntaxKind.ClassDeclaration || // class A + +////let v = [1,2,3,4]; +////let x = [.../**/ + +goTo.marker(); +verify.completionListContains("v"); \ No newline at end of file diff --git a/tests/cases/fourslash/completionListBuilderLocations_Modules.ts b/tests/cases/fourslash/completionListBuilderLocations_Modules.ts index eac7085ab66..0552e540a7e 100644 --- a/tests/cases/fourslash/completionListBuilderLocations_Modules.ts +++ b/tests/cases/fourslash/completionListBuilderLocations_Modules.ts @@ -5,9 +5,8 @@ ////module A./*moduleName2*/ +goTo.marker("moduleName1"); +verify.not.completionListIsEmpty(); -test.markers().forEach((m) => { - goTo.position(m.position, m.fileName); - verify.not.completionListIsEmpty(); - verify.completionListAllowsNewIdentifier(); -}); \ No newline at end of file +goTo.marker("moduleName2"); +verify.completionListIsEmpty(); diff --git a/tests/cases/fourslash/memberListAfterDoubleDot.ts b/tests/cases/fourslash/memberListAfterDoubleDot.ts new file mode 100644 index 00000000000..21a1ad913ea --- /dev/null +++ b/tests/cases/fourslash/memberListAfterDoubleDot.ts @@ -0,0 +1,6 @@ +/// + +////../**/ + +goTo.marker(); +verify.memberListIsEmpty(); \ No newline at end of file diff --git a/tests/cases/fourslash/memberListAfterSingleDot.ts b/tests/cases/fourslash/memberListAfterSingleDot.ts index 0bc5107f2c1..62edf3b8c9e 100644 --- a/tests/cases/fourslash/memberListAfterSingleDot.ts +++ b/tests/cases/fourslash/memberListAfterSingleDot.ts @@ -3,4 +3,4 @@ ////./**/ goTo.marker(); -verify.not.memberListIsEmpty(); \ No newline at end of file +verify.memberListIsEmpty(); \ No newline at end of file diff --git a/tests/cases/fourslash/tsxCompletion4.ts b/tests/cases/fourslash/tsxCompletion4.ts new file mode 100644 index 00000000000..6a66c4a898f --- /dev/null +++ b/tests/cases/fourslash/tsxCompletion4.ts @@ -0,0 +1,14 @@ +/// + +//@Filename: file.tsx +//// declare namespace JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { one; two; } +//// } +//// } +//// let bag = { x: 100, y: 200 }; +////