@@ -238,7 +238,7 @@ namespace ts.Completions {
238
238
239
239
function getStringLiteralCompletionEntries ( sourceFile : SourceFile , position : number , typeChecker : TypeChecker , compilerOptions : CompilerOptions , host : LanguageServiceHost , log : Log ) : CompletionInfo | undefined {
240
240
const node = findPrecedingToken ( position , sourceFile ) ;
241
- if ( ! node || ( node . kind !== SyntaxKind . StringLiteral && node . kind !== SyntaxKind . NoSubstitutionTemplateLiteral ) ) {
241
+ if ( ! node || ! isStringLiteral ( node ) && ! isNoSubstitutionTemplateLiteral ( node ) ) {
242
242
return undefined ;
243
243
}
244
244
@@ -277,21 +277,9 @@ namespace ts.Completions {
277
277
// import x = require("/*completion position*/");
278
278
// var y = require("/*completion position*/");
279
279
// export * from "/*completion position*/";
280
- const entries = PathCompletions . getStringLiteralCompletionsFromModuleNames ( < StringLiteral > node , compilerOptions , host , typeChecker ) ;
280
+ const entries = PathCompletions . getStringLiteralCompletionsFromModuleNames ( node , compilerOptions , host , typeChecker ) ;
281
281
return pathCompletionsInfo ( entries ) ;
282
282
}
283
- else if ( isEqualityExpression ( node . parent ) ) {
284
- // Get completions from the type of the other operand
285
- // i.e. switch (a) {
286
- // case '/*completion position*/'
287
- // }
288
- return getStringLiteralCompletionEntriesFromType ( typeChecker . getTypeAtLocation ( node . parent . left === node ? node . parent . right : node . parent . left ) , typeChecker ) ;
289
- }
290
- else if ( isCaseOrDefaultClause ( node . parent ) ) {
291
- // Get completions from the type of the switch expression
292
- // i.e. x === '/*completion position'
293
- return getStringLiteralCompletionEntriesFromType ( typeChecker . getTypeAtLocation ( ( < SwitchStatement > node . parent . parent . parent ) . expression ) , typeChecker ) ;
294
- }
295
283
else {
296
284
const argumentInfo = SignatureHelp . getImmediatelyContainingArgumentInfo ( node , position , sourceFile ) ;
297
285
if ( argumentInfo ) {
@@ -303,7 +291,7 @@ namespace ts.Completions {
303
291
304
292
// Get completion for string literal from string literal type
305
293
// i.e. var x: "hi" | "hello" = "/*completion position*/"
306
- return getStringLiteralCompletionEntriesFromType ( typeChecker . getContextualType ( < LiteralExpression > node ) , typeChecker ) ;
294
+ return getStringLiteralCompletionEntriesFromType ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) ;
307
295
}
308
296
}
309
297
@@ -602,15 +590,57 @@ namespace ts.Completions {
602
590
}
603
591
type Request = { kind : "JsDocTagName" } | { kind : "JsDocTag" } | { kind : "JsDocParameterName" , tag : JSDocParameterTag } ;
604
592
605
- function getRecommendedCompletion ( currentToken : Node , checker : TypeChecker /*, symbolToOriginInfoMap: SymbolOriginInfoMap*/ ) : Symbol | undefined {
606
- const ty = checker . getContextualType ( currentToken as Expression ) ;
593
+ function getRecommendedCompletion ( currentToken : Node , checker : TypeChecker ) : Symbol | undefined {
594
+ const ty = getContextualType ( currentToken , checker ) ;
607
595
const symbol = ty && ty . symbol ;
608
596
// Don't include make a recommended completion for an abstract class
609
597
return symbol && ( symbol . flags & SymbolFlags . Enum || symbol . flags & SymbolFlags . Class && ! isAbstractConstructorSymbol ( symbol ) )
610
598
? getFirstSymbolInChain ( symbol , currentToken , checker )
611
599
: undefined ;
612
600
}
613
601
602
+ function getContextualType ( currentToken : Node , checker : ts . TypeChecker ) : Type | undefined {
603
+ const { parent } = currentToken ;
604
+ switch ( currentToken . kind ) {
605
+ case ts . SyntaxKind . Identifier :
606
+ return getContextualTypeFromParent ( currentToken as ts . Identifier , checker ) ;
607
+ case ts . SyntaxKind . EqualsToken :
608
+ return ts . isVariableDeclaration ( parent ) ? checker . getContextualType ( parent . initializer ) :
609
+ ts . isBinaryExpression ( parent ) ? checker . getTypeAtLocation ( parent . left ) : undefined ;
610
+ case ts . SyntaxKind . NewKeyword :
611
+ return checker . getContextualType ( parent as ts . Expression ) ;
612
+ case ts . SyntaxKind . CaseKeyword :
613
+ return getSwitchedType ( cast ( currentToken . parent , isCaseClause ) , checker ) ;
614
+ default :
615
+ return isEqualityOperatorKind ( currentToken . kind ) && ts . isBinaryExpression ( parent ) && isEqualityOperatorKind ( parent . operatorToken . kind )
616
+ // completion at `x ===/**/` should be for the right side
617
+ ? checker . getTypeAtLocation ( parent . left )
618
+ : checker . getContextualType ( currentToken as ts . Expression ) ;
619
+ }
620
+ }
621
+
622
+ function getContextualTypeFromParent ( node : ts . Expression , checker : ts . TypeChecker ) : Type | undefined {
623
+ const { parent } = node ;
624
+ switch ( parent . kind ) {
625
+ case ts . SyntaxKind . NewExpression :
626
+ return checker . getContextualType ( parent as ts . NewExpression ) ;
627
+ case ts . SyntaxKind . BinaryExpression : {
628
+ const { left, operatorToken, right } = parent as ts . BinaryExpression ;
629
+ return isEqualityOperatorKind ( operatorToken . kind )
630
+ ? checker . getTypeAtLocation ( node === right ? left : right )
631
+ : checker . getContextualType ( node ) ;
632
+ }
633
+ case ts . SyntaxKind . CaseClause :
634
+ return ( parent as ts . CaseClause ) . expression === node ? getSwitchedType ( parent as ts . CaseClause , checker ) : undefined ;
635
+ default :
636
+ return checker . getContextualType ( node ) ;
637
+ }
638
+ }
639
+
640
+ function getSwitchedType ( caseClause : ts . CaseClause , checker : ts . TypeChecker ) : ts . Type {
641
+ return checker . getTypeAtLocation ( caseClause . parent . parent . expression ) ;
642
+ }
643
+
614
644
function getFirstSymbolInChain ( symbol : Symbol , enclosingDeclaration : Node , checker : TypeChecker ) : Symbol | undefined {
615
645
const chain = checker . getAccessibleSymbolChain ( symbol , enclosingDeclaration , /*meaning*/ SymbolFlags . All , /*useOnlyExternalAliasing*/ false ) ;
616
646
if ( chain ) return first ( chain ) ;
@@ -851,7 +881,7 @@ namespace ts.Completions {
851
881
852
882
log ( "getCompletionData: Semantic work: " + ( timestamp ( ) - semanticStart ) ) ;
853
883
854
- const recommendedCompletion = getRecommendedCompletion ( previousToken , typeChecker ) ;
884
+ const recommendedCompletion = previousToken && getRecommendedCompletion ( previousToken , typeChecker ) ;
855
885
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken } ;
856
886
857
887
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
@@ -2081,15 +2111,16 @@ namespace ts.Completions {
2081
2111
return isConstructorParameterCompletionKeyword ( stringToToken ( text ) ) ;
2082
2112
}
2083
2113
2084
- function isEqualityExpression ( node : Node ) : node is BinaryExpression {
2085
- return isBinaryExpression ( node ) && isEqualityOperatorKind ( node . operatorToken . kind ) ;
2086
- }
2087
-
2088
- function isEqualityOperatorKind ( kind : SyntaxKind ) {
2089
- return kind === SyntaxKind . EqualsEqualsToken ||
2090
- kind === SyntaxKind . ExclamationEqualsToken ||
2091
- kind === SyntaxKind . EqualsEqualsEqualsToken ||
2092
- kind === SyntaxKind . ExclamationEqualsEqualsToken ;
2114
+ function isEqualityOperatorKind ( kind : ts . SyntaxKind ) : kind is EqualityOperator {
2115
+ switch ( kind ) {
2116
+ case ts . SyntaxKind . EqualsEqualsEqualsToken :
2117
+ case ts . SyntaxKind . EqualsEqualsToken :
2118
+ case ts . SyntaxKind . ExclamationEqualsEqualsToken :
2119
+ case ts . SyntaxKind . ExclamationEqualsToken :
2120
+ return true ;
2121
+ default :
2122
+ return false ;
2123
+ }
2093
2124
}
2094
2125
2095
2126
/** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
0 commit comments