@@ -18,7 +18,7 @@ namespace ts.Completions {
18
18
return undefined ;
19
19
}
20
20
21
- const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag } = completionData ;
21
+ const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } = completionData ;
22
22
23
23
if ( requestJsDocTagName ) {
24
24
// If the current position is a jsDoc tag name, only tag names should be provided for completion
@@ -52,16 +52,19 @@ namespace ts.Completions {
52
52
sortText : "0" ,
53
53
} ) ;
54
54
}
55
- else {
55
+ else if ( ! hasFilteredClassMemberKeywords ) {
56
56
return undefined ;
57
57
}
58
58
}
59
59
60
60
getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true , typeChecker , compilerOptions . target , log ) ;
61
61
}
62
62
63
+ if ( hasFilteredClassMemberKeywords ) {
64
+ addRange ( entries , classMemberKeywordCompletions ) ;
65
+ }
63
66
// Add keywords if this is not a member completion list
64
- if ( ! isMemberCompletion && ! requestJsDocTag && ! requestJsDocTagName ) {
67
+ else if ( ! isMemberCompletion && ! requestJsDocTag && ! requestJsDocTagName ) {
65
68
addRange ( entries , keywordCompletions ) ;
66
69
}
67
70
@@ -406,7 +409,7 @@ namespace ts.Completions {
406
409
}
407
410
408
411
if ( requestJsDocTagName || requestJsDocTag ) {
409
- return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , requestJsDocTagName, requestJsDocTag } ;
412
+ return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords : false } ;
410
413
}
411
414
412
415
if ( ! insideJsDocTagExpression ) {
@@ -505,6 +508,7 @@ namespace ts.Completions {
505
508
let isGlobalCompletion = false ;
506
509
let isMemberCompletion : boolean ;
507
510
let isNewIdentifierLocation : boolean ;
511
+ let hasFilteredClassMemberKeywords = false ;
508
512
let symbols : Symbol [ ] = [ ] ;
509
513
510
514
if ( isRightOfDot ) {
@@ -542,7 +546,7 @@ namespace ts.Completions {
542
546
543
547
log ( "getCompletionData: Semantic work: " + ( timestamp ( ) - semanticStart ) ) ;
544
548
545
- return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , requestJsDocTagName, requestJsDocTag } ;
549
+ return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , requestJsDocTagName, requestJsDocTag, hasFilteredClassMemberKeywords } ;
546
550
547
551
function getTypeScriptMemberSymbols ( ) : void {
548
552
// Right of dot member completion list
@@ -599,6 +603,7 @@ namespace ts.Completions {
599
603
function tryGetGlobalSymbols ( ) : boolean {
600
604
let objectLikeContainer : ObjectLiteralExpression | BindingPattern ;
601
605
let namedImportsOrExports : NamedImportsOrExports ;
606
+ let classLikeContainer : ClassLikeDeclaration ;
602
607
let jsxContainer : JsxOpeningLikeElement ;
603
608
604
609
if ( objectLikeContainer = tryGetObjectLikeCompletionContainer ( contextToken ) ) {
@@ -611,6 +616,11 @@ namespace ts.Completions {
611
616
return tryGetImportOrExportClauseCompletionSymbols ( namedImportsOrExports ) ;
612
617
}
613
618
619
+ if ( classLikeContainer = tryGetClassLikeCompletionContainer ( contextToken ) ) {
620
+ // cursor inside class declaration
621
+ return tryGetClassLikeCompletionSymbols ( classLikeContainer ) ;
622
+ }
623
+
614
624
if ( jsxContainer = tryGetContainingJsxElement ( contextToken ) ) {
615
625
let attrsType : Type ;
616
626
if ( ( jsxContainer . kind === SyntaxKind . JsxSelfClosingElement ) || ( jsxContainer . kind === SyntaxKind . JsxOpeningElement ) ) {
@@ -913,6 +923,31 @@ namespace ts.Completions {
913
923
return true ;
914
924
}
915
925
926
+ /**
927
+ * Aggregates relevant symbols for completion in class declaration
928
+ * Relevant symbols are stored in the captured 'symbols' variable.
929
+ *
930
+ * @returns true if 'symbols' was successfully populated; false otherwise.
931
+ */
932
+ function tryGetClassLikeCompletionSymbols ( classLikeDeclaration : ClassLikeDeclaration ) : boolean {
933
+ // We're looking up possible property names from parent type.
934
+ isMemberCompletion = true ;
935
+ // Declaring new property/method/accessor
936
+ isNewIdentifierLocation = true ;
937
+ // Has keywords for class elements
938
+ hasFilteredClassMemberKeywords = true ;
939
+
940
+ const baseTypeNode = getClassExtendsHeritageClauseElement ( classLikeDeclaration ) ;
941
+ if ( baseTypeNode ) {
942
+ const baseType = typeChecker . getTypeAtLocation ( baseTypeNode ) ;
943
+ // List of property symbols of base type that are not private
944
+ symbols = filter ( typeChecker . getPropertiesOfType ( baseType ) ,
945
+ baseProperty => ! ( getDeclarationModifierFlagsFromSymbol ( baseProperty ) & ModifierFlags . Private ) ) ;
946
+ }
947
+
948
+ return true ;
949
+ }
950
+
916
951
/**
917
952
* Returns the immediate owning object literal or binding pattern of a context token,
918
953
* on the condition that one exists and that the context implies completion should be given.
@@ -953,6 +988,38 @@ namespace ts.Completions {
953
988
return undefined ;
954
989
}
955
990
991
+ /**
992
+ * Returns the immediate owning class declaration of a context token,
993
+ * on the condition that one exists and that the context implies completion should be given.
994
+ */
995
+ function tryGetClassLikeCompletionContainer ( contextToken : Node ) : ClassLikeDeclaration {
996
+ if ( contextToken ) {
997
+ switch ( contextToken . kind ) {
998
+ case SyntaxKind . OpenBraceToken : // class c { |
999
+ if ( isClassLike ( contextToken . parent ) ) {
1000
+ return contextToken . parent ;
1001
+ }
1002
+ break ;
1003
+
1004
+ // class c {getValue(): number; | }
1005
+ case SyntaxKind . CommaToken :
1006
+ case SyntaxKind . SemicolonToken :
1007
+ // class c { method() { } | }
1008
+ case SyntaxKind . CloseBraceToken :
1009
+ if ( isClassLike ( location ) ) {
1010
+ return location ;
1011
+ }
1012
+ break ;
1013
+ }
1014
+ }
1015
+
1016
+ // class c { method() { } | method2() { } }
1017
+ if ( location && location . kind === SyntaxKind . SyntaxList && isClassLike ( location . parent ) ) {
1018
+ return location . parent ;
1019
+ }
1020
+ return undefined ;
1021
+ }
1022
+
956
1023
function tryGetContainingJsxElement ( contextToken : Node ) : JsxOpeningLikeElement {
957
1024
if ( contextToken ) {
958
1025
const parent = contextToken . parent ;
@@ -1306,6 +1373,21 @@ namespace ts.Completions {
1306
1373
} ) ;
1307
1374
}
1308
1375
1376
+ const classMemberKeywordCompletions = filter ( keywordCompletions , entry => {
1377
+ switch ( stringToToken ( entry . name ) ) {
1378
+ case SyntaxKind . PublicKeyword :
1379
+ case SyntaxKind . ProtectedKeyword :
1380
+ case SyntaxKind . PrivateKeyword :
1381
+ case SyntaxKind . AbstractKeyword :
1382
+ case SyntaxKind . StaticKeyword :
1383
+ case SyntaxKind . ConstructorKeyword :
1384
+ case SyntaxKind . ReadonlyKeyword :
1385
+ case SyntaxKind . GetKeyword :
1386
+ case SyntaxKind . SetKeyword :
1387
+ return true ;
1388
+ }
1389
+ } ) ;
1390
+
1309
1391
function isEqualityExpression ( node : Node ) : node is BinaryExpression {
1310
1392
return isBinaryExpression ( node ) && isEqualityOperatorKind ( node . operatorToken . kind ) ;
1311
1393
}
0 commit comments