@@ -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 ) {
241
+ if ( ! node || ! isStringLiteral ( node ) ) {
242
242
return undefined ;
243
243
}
244
244
@@ -280,18 +280,6 @@ namespace ts.Completions {
280
280
const entries = PathCompletions . getStringLiteralCompletionsFromModuleNames ( < StringLiteral > 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 ( < StringLiteral > node ) , typeChecker ) ;
294
+ return getStringLiteralCompletionEntriesFromType ( getContextualTypeFromParent ( node , typeChecker ) , typeChecker ) ;
307
295
}
308
296
}
309
297
@@ -600,15 +588,60 @@ namespace ts.Completions {
600
588
}
601
589
type Request = { kind : "JsDocTagName" } | { kind : "JsDocTag" } | { kind : "JsDocParameterName" , tag : JSDocParameterTag } ;
602
590
603
- function getRecommendedCompletion ( currentToken : Node , checker : TypeChecker /*, symbolToOriginInfoMap: SymbolOriginInfoMap*/ ) : Symbol | undefined {
604
- const ty = checker . getContextualType ( currentToken as Expression ) ;
591
+ function getRecommendedCompletion ( currentToken : Node , checker : TypeChecker ) : Symbol | undefined {
592
+ const ty = getContextualType ( currentToken , checker ) ;
605
593
const symbol = ty && ty . symbol ;
606
594
// Don't include make a recommended completion for an abstract class
607
595
return symbol && ( symbol . flags & SymbolFlags . Enum || symbol . flags & SymbolFlags . Class && ! isAbstractConstructorSymbol ( symbol ) )
608
596
? getFirstSymbolInChain ( symbol , currentToken , checker )
609
597
: undefined ;
610
598
}
611
599
600
+ function getContextualType ( currentToken : Node , checker : ts . TypeChecker ) : Type | undefined {
601
+ const { parent } = currentToken ;
602
+ switch ( currentToken . kind ) {
603
+ case ts . SyntaxKind . Identifier :
604
+ return getContextualTypeFromParent ( currentToken as ts . Identifier , checker ) ;
605
+ case ts . SyntaxKind . EqualsToken :
606
+ return ts . isVariableDeclaration ( parent )
607
+ ? checker . getContextualType ( parent . initializer )
608
+ : ts . isBinaryExpression ( parent )
609
+ ? checker . getTypeAtLocation ( parent . left )
610
+ : undefined ;
611
+ case ts . SyntaxKind . NewKeyword :
612
+ return checker . getContextualType ( parent as ts . Expression ) ;
613
+ case ts . SyntaxKind . CaseKeyword :
614
+ return getSwitchedType ( cast ( currentToken . parent , isCaseClause ) , checker ) ;
615
+ default :
616
+ return isEqualityOperatorKind ( currentToken . kind ) && ts . isBinaryExpression ( parent ) && isEqualityOperatorKind ( parent . operatorToken . kind )
617
+ // completion at `x ===/**/` should be for the right side
618
+ ? checker . getTypeAtLocation ( parent . left )
619
+ : checker . getContextualType ( currentToken as ts . Expression ) ;
620
+ }
621
+ }
622
+
623
+ function getContextualTypeFromParent ( node : ts . Expression , checker : ts . TypeChecker ) : Type | undefined {
624
+ const { parent } = node ;
625
+ switch ( parent . kind ) {
626
+ case ts . SyntaxKind . NewExpression :
627
+ return checker . getContextualType ( parent as ts . NewExpression ) ;
628
+ case ts . SyntaxKind . BinaryExpression : {
629
+ const { left, operatorToken, right } = parent as ts . BinaryExpression ;
630
+ return isEqualityOperatorKind ( operatorToken . kind )
631
+ ? checker . getTypeAtLocation ( node === right ? left : right )
632
+ : checker . getContextualType ( node ) ;
633
+ }
634
+ case ts . SyntaxKind . CaseClause :
635
+ return ( parent as ts . CaseClause ) . expression === node ? getSwitchedType ( parent as ts . CaseClause , checker ) : undefined ;
636
+ default :
637
+ return checker . getContextualType ( node ) ;
638
+ }
639
+ }
640
+
641
+ function getSwitchedType ( caseClause : ts . CaseClause , checker : ts . TypeChecker ) : ts . Type {
642
+ return checker . getTypeAtLocation ( caseClause . parent . parent . expression ) ;
643
+ }
644
+
612
645
function getFirstSymbolInChain ( symbol : Symbol , enclosingDeclaration : Node , checker : TypeChecker ) : Symbol | undefined {
613
646
const chain = checker . getAccessibleSymbolChain ( symbol , enclosingDeclaration , /*meaning*/ SymbolFlags . All , /*useOnlyExternalAliasing*/ false ) ;
614
647
if ( chain ) return first ( chain ) ;
@@ -848,7 +881,7 @@ namespace ts.Completions {
848
881
849
882
log ( "getCompletionData: Semantic work: " + ( timestamp ( ) - semanticStart ) ) ;
850
883
851
- const recommendedCompletion = getRecommendedCompletion ( previousToken , typeChecker ) ;
884
+ const recommendedCompletion = previousToken && getRecommendedCompletion ( previousToken , typeChecker ) ;
852
885
return { symbols, isGlobalCompletion, isMemberCompletion, allowStringLiteral, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, keywordFilters, symbolToOriginInfoMap, recommendedCompletion } ;
853
886
854
887
type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
@@ -2078,15 +2111,16 @@ namespace ts.Completions {
2078
2111
return isConstructorParameterCompletionKeyword ( stringToToken ( text ) ) ;
2079
2112
}
2080
2113
2081
- function isEqualityExpression ( node : Node ) : node is BinaryExpression {
2082
- return isBinaryExpression ( node ) && isEqualityOperatorKind ( node . operatorToken . kind ) ;
2083
- }
2084
-
2085
- function isEqualityOperatorKind ( kind : SyntaxKind ) {
2086
- return kind === SyntaxKind . EqualsEqualsToken ||
2087
- kind === SyntaxKind . ExclamationEqualsToken ||
2088
- kind === SyntaxKind . EqualsEqualsEqualsToken ||
2089
- 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
+ }
2090
2124
}
2091
2125
2092
2126
/** Get the corresponding JSDocTag node if the position is in a jsDoc comment */
0 commit comments