diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 30d4818592b..f02520b24fd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -61,6 +61,7 @@ namespace ts { getTypeCount: () => typeCount, isUndefinedSymbol: symbol => symbol === undefinedSymbol, isArgumentsSymbol: symbol => symbol === argumentsSymbol, + isUnknownSymbol: symbol => symbol === unknownSymbol, getDiagnostics, getGlobalDiagnostics, @@ -7979,6 +7980,7 @@ namespace ts { if (compilerOptions.noImplicitAny) { error(node, Diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements); } + return unknownSymbol; } } @@ -14574,7 +14576,7 @@ namespace ts { return false; } - function getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[] { + function getSymbolsInScope(location: Node, meaning: SymbolFlags, includeAllGlobalSymbols: boolean): Symbol[] { const symbols: SymbolTable = {}; let memberFlags: NodeFlags = 0; @@ -14637,7 +14639,9 @@ namespace ts { location = location.parent; } - copySymbols(globals, meaning); + if (includeAllGlobalSymbols) { + copySymbols(globals, meaning); + } } /** diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 9411437c981..eb93ceee093 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1717,7 +1717,7 @@ namespace ts { getBaseTypes(type: InterfaceType): ObjectType[]; getReturnTypeOfSignature(signature: Signature): Type; - getSymbolsInScope(location: Node, meaning: SymbolFlags): Symbol[]; + getSymbolsInScope(location: Node, meaning: SymbolFlags, includeAllGlobalSymbols: boolean): Symbol[]; getSymbolAtLocation(node: Node): Symbol; getShorthandAssignmentValueSymbol(location: Node): Symbol; getTypeAtLocation(node: Node): Type; @@ -1733,6 +1733,7 @@ namespace ts { isImplementationOfOverload(node: FunctionLikeDeclaration): boolean; isUndefinedSymbol(symbol: Symbol): boolean; isArgumentsSymbol(symbol: Symbol): boolean; + isUnknownSymbol(symbol: Symbol): boolean; getConstantValue(node: EnumMember | PropertyAccessExpression | ElementAccessExpression): number; isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean; diff --git a/src/services/services.ts b/src/services/services.ts index a9dda549ead..7e12a521a4d 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3099,6 +3099,7 @@ namespace ts { } else if (kind === SyntaxKind.SlashToken && contextToken.parent.kind === SyntaxKind.JsxClosingElement) { isStartingCloseTag = true; + location = contextToken; } } } @@ -3113,7 +3114,9 @@ namespace ts { } else if (isRightOfOpenTag) { let tagSymbols = typeChecker.getJsxIntrinsicTagNames(); - if (tryGetGlobalSymbols()) { + // If the currect cursor is inside JSX opening tag, the only meaningful completions are those of JSX.IntrinsicElements or users defined React.Component + // If the services can't find those symbols, then show nothing instead of including all the global symbols in the completion list. + if (tryGetGlobalSymbols(/*includeAllGlobalSymbols*/false)) { symbols = tagSymbols.concat(symbols.filter(s => !!(s.flags & SymbolFlags.Value))); } else { @@ -3123,8 +3126,12 @@ namespace ts { isNewIdentifierLocation = false; } else if (isStartingCloseTag) { - let tagName = (contextToken.parent.parent).openingElement.tagName; - symbols = [typeChecker.getSymbolAtLocation(tagName)]; + const tagName = (contextToken.parent.parent).openingElement.tagName; + const tagSymbol = typeChecker.getSymbolAtLocation(tagName); + + if (!typeChecker.isUnknownSymbol(tagSymbol)) { + symbols = [tagSymbol]; + } isMemberCompletion = true; isNewIdentifierLocation = false; @@ -3133,7 +3140,7 @@ namespace ts { // For JavaScript or TypeScript, if we're not after a dot, then just try to get the // global symbols in scope. These results should be valid for either language as // the set of symbols that can be referenced from this location. - if (!tryGetGlobalSymbols()) { + if (!tryGetGlobalSymbols(/*includeAllGlobalSymbols*/true)) { return undefined; } } @@ -3193,7 +3200,7 @@ namespace ts { } } - function tryGetGlobalSymbols(): boolean { + function tryGetGlobalSymbols(includeAllGlobalSymbols: boolean): boolean { let objectLikeContainer: ObjectLiteralExpression | BindingPattern; let namedImportsOrExports: NamedImportsOrExports; let jsxContainer: JsxOpeningLikeElement; @@ -3264,7 +3271,7 @@ namespace ts { /// TODO filter meaning based on the current context let symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias; - symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings); + symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings, includeAllGlobalSymbols); return true; } @@ -3831,7 +3838,23 @@ namespace ts { } else { if (!symbols || symbols.length === 0) { - return undefined; + if (sourceFile.languageVariant === LanguageVariant.JSX && + location.parent && location.parent.kind === SyntaxKind.JsxClosingElement) { + // In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag, + // instead of simply giving unknown value, the completion will return the tag-name of an associated opening-element. + // For example: + // var x =
completion list at "1" will contain "div" with type any + const tagName = (location.parent.parent).openingElement.tagName; + entries.push({ + name: (tagName).text, + kind: undefined, + kindModifiers: undefined, + sortText: "0", + }); + } + else { + return undefined; + } } getCompletionEntriesFromSymbols(symbols, entries); @@ -3907,6 +3930,7 @@ namespace ts { function getCompletionEntriesFromSymbols(symbols: Symbol[], entries: CompletionEntry[]): Map { let start = new Date().getTime(); let uniqueNames: Map = {}; + if (symbols) { for (let symbol of symbols) { let entry = createCompletionEntry(symbol, location); @@ -4439,7 +4463,7 @@ namespace ts { let typeChecker = program.getTypeChecker(); let symbol = typeChecker.getSymbolAtLocation(node); - if (!symbol) { + if (!symbol || typeChecker.isUnknownSymbol(symbol)) { // Try getting just type at this position and show switch (node.kind) { case SyntaxKind.Identifier: