4
4
namespace ts . Completions {
5
5
export type Log = ( message : string ) => void ;
6
6
7
+ const enum KeywordCompletionFilters {
8
+ None ,
9
+ ClassElementKeywords , // Keywords at class keyword
10
+ ConstructorParameterKeywords , // Keywords at constructor parameter
11
+ }
12
+
7
13
export function getCompletionsAtPosition ( host : LanguageServiceHost , typeChecker : TypeChecker , log : Log , compilerOptions : CompilerOptions , sourceFile : SourceFile , position : number ) : CompletionInfo | undefined {
8
14
if ( isInReferenceComment ( sourceFile , position ) ) {
9
15
return PathCompletions . getTripleSlashReferenceCompletion ( sourceFile , position , compilerOptions , host ) ;
@@ -18,7 +24,7 @@ namespace ts.Completions {
18
24
return undefined ;
19
25
}
20
26
21
- const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, request, hasFilteredClassMemberKeywords } = completionData ;
27
+ const { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, request, keywordFilters } = completionData ;
22
28
23
29
if ( sourceFile . languageVariant === LanguageVariant . JSX &&
24
30
location && location . parent && location . parent . kind === SyntaxKind . JsxClosingElement ) {
@@ -54,21 +60,20 @@ namespace ts.Completions {
54
60
addRange ( entries , getJavaScriptCompletionEntries ( sourceFile , location . pos , uniqueNames , compilerOptions . target ) ) ;
55
61
}
56
62
else {
57
- if ( ! symbols || symbols . length === 0 ) {
58
- if ( ! hasFilteredClassMemberKeywords ) {
63
+ if ( ( ! symbols || symbols . length === 0 ) && keywordFilters === KeywordCompletionFilters . None ) {
59
64
return undefined ;
60
- }
61
65
}
62
66
63
67
getCompletionEntriesFromSymbols ( symbols , entries , location , /*performCharacterChecks*/ true , typeChecker , compilerOptions . target , log ) ;
64
68
}
65
69
66
- if ( hasFilteredClassMemberKeywords ) {
67
- addRange ( entries , classMemberKeywordCompletions ) ;
68
- }
69
- // Add keywords if this is not a member completion list
70
- else if ( ! isMemberCompletion ) {
71
- addRange ( entries , keywordCompletions ) ;
70
+ // TODO add filter for keyword based on type/value/namespace and also location
71
+
72
+ // Add all keywords if
73
+ // - this is not a member completion list (all the keywords)
74
+ // - other filters are enabled in required scenario so add those keywords
75
+ if ( keywordFilters !== KeywordCompletionFilters . None || ! isMemberCompletion ) {
76
+ addRange ( entries , getKeywordCompletions ( keywordFilters ) ) ;
72
77
}
73
78
74
79
return { isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation : isNewIdentifierLocation , entries } ;
@@ -317,7 +322,10 @@ namespace ts.Completions {
317
322
}
318
323
319
324
// Didn't find a symbol with this name. See if we can find a keyword instead.
320
- const keywordCompletion = forEach ( keywordCompletions , c => c . name === entryName ) ;
325
+ const keywordCompletion = forEach (
326
+ getKeywordCompletions ( KeywordCompletionFilters . None ) ,
327
+ c => c . name === entryName
328
+ ) ;
321
329
if ( keywordCompletion ) {
322
330
return {
323
331
name : entryName ,
@@ -356,7 +364,7 @@ namespace ts.Completions {
356
364
location : Node ;
357
365
isRightOfDot : boolean ;
358
366
request ?: Request ;
359
- hasFilteredClassMemberKeywords : boolean ;
367
+ keywordFilters : KeywordCompletionFilters ;
360
368
}
361
369
type Request = { kind : "JsDocTagName" } | { kind : "JsDocTag" } | { kind : "JsDocParameterName" , tag : JSDocParameterTag } ;
362
370
@@ -432,7 +440,7 @@ namespace ts.Completions {
432
440
}
433
441
434
442
if ( request ) {
435
- return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , request, hasFilteredClassMemberKeywords : false } ;
443
+ return { symbols : undefined , isGlobalCompletion : false , isMemberCompletion : false , isNewIdentifierLocation : false , location : undefined , isRightOfDot : false , request, keywordFilters : KeywordCompletionFilters . None } ;
436
444
}
437
445
438
446
if ( ! insideJsDocTagTypeExpression ) {
@@ -531,7 +539,7 @@ namespace ts.Completions {
531
539
let isGlobalCompletion = false ;
532
540
let isMemberCompletion : boolean ;
533
541
let isNewIdentifierLocation : boolean ;
534
- let hasFilteredClassMemberKeywords = false ;
542
+ let keywordFilters = KeywordCompletionFilters . None ;
535
543
let symbols : Symbol [ ] = [ ] ;
536
544
537
545
if ( isRightOfDot ) {
@@ -569,7 +577,7 @@ namespace ts.Completions {
569
577
570
578
log ( "getCompletionData: Semantic work: " + ( timestamp ( ) - semanticStart ) ) ;
571
579
572
- return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, hasFilteredClassMemberKeywords } ;
580
+ return { symbols, isGlobalCompletion, isMemberCompletion, isNewIdentifierLocation, location, isRightOfDot : ( isRightOfDot || isRightOfOpenTag ) , request, keywordFilters } ;
573
581
574
582
type JSDocTagWithTypeExpression = JSDocAugmentsTag | JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag ;
575
583
@@ -664,6 +672,16 @@ namespace ts.Completions {
664
672
return tryGetImportOrExportClauseCompletionSymbols ( namedImportsOrExports ) ;
665
673
}
666
674
675
+ if ( tryGetConstructorLikeCompletionContainer ( contextToken ) ) {
676
+ // no members, only keywords
677
+ isMemberCompletion = false ;
678
+ // Declaring new property/method/accessor
679
+ isNewIdentifierLocation = true ;
680
+ // Has keywords for constructor parameter
681
+ keywordFilters = KeywordCompletionFilters . ConstructorParameterKeywords ;
682
+ return true ;
683
+ }
684
+
667
685
if ( classLikeContainer = tryGetClassLikeCompletionContainer ( contextToken ) ) {
668
686
// cursor inside class declaration
669
687
getGetClassLikeCompletionSymbols ( classLikeContainer ) ;
@@ -1046,7 +1064,7 @@ namespace ts.Completions {
1046
1064
// Declaring new property/method/accessor
1047
1065
isNewIdentifierLocation = true ;
1048
1066
// Has keywords for class elements
1049
- hasFilteredClassMemberKeywords = true ;
1067
+ keywordFilters = KeywordCompletionFilters . ClassElementKeywords ;
1050
1068
1051
1069
const baseTypeNode = getClassExtendsHeritageClauseElement ( classLikeDeclaration ) ;
1052
1070
const implementsTypeNodes = getClassImplementsHeritageClauseElements ( classLikeDeclaration ) ;
@@ -1136,6 +1154,16 @@ namespace ts.Completions {
1136
1154
return isClassElement ( node . parent ) && isClassLike ( node . parent . parent ) ;
1137
1155
}
1138
1156
1157
+ function isParameterOfConstructorDeclaration ( node : Node ) {
1158
+ return isParameter ( node ) && isConstructorDeclaration ( node . parent ) ;
1159
+ }
1160
+
1161
+ function isConstructorParameterCompletion ( node : Node ) {
1162
+ return node . parent &&
1163
+ isParameterOfConstructorDeclaration ( node . parent ) &&
1164
+ ( isConstructorParameterCompletionKeyword ( node . kind ) || isDeclarationName ( node ) ) ;
1165
+ }
1166
+
1139
1167
/**
1140
1168
* Returns the immediate owning class declaration of a context token,
1141
1169
* on the condition that one exists and that the context implies completion should be given.
@@ -1149,8 +1177,14 @@ namespace ts.Completions {
1149
1177
}
1150
1178
break ;
1151
1179
1152
- // class c {getValue(): number; | }
1180
+ // class c {getValue(): number, | }
1153
1181
case SyntaxKind . CommaToken :
1182
+ if ( isClassLike ( contextToken . parent ) ) {
1183
+ return contextToken . parent ;
1184
+ }
1185
+ break ;
1186
+
1187
+ // class c {getValue(): number; | }
1154
1188
case SyntaxKind . SemicolonToken :
1155
1189
// class c { method() { } | }
1156
1190
case SyntaxKind . CloseBraceToken :
@@ -1175,6 +1209,26 @@ namespace ts.Completions {
1175
1209
return undefined ;
1176
1210
}
1177
1211
1212
+ /**
1213
+ * Returns the immediate owning class declaration of a context token,
1214
+ * on the condition that one exists and that the context implies completion should be given.
1215
+ */
1216
+ function tryGetConstructorLikeCompletionContainer ( contextToken : Node ) : ConstructorDeclaration {
1217
+ if ( contextToken ) {
1218
+ switch ( contextToken . kind ) {
1219
+ case SyntaxKind . OpenParenToken :
1220
+ case SyntaxKind . CommaToken :
1221
+ return isConstructorDeclaration ( contextToken . parent ) && contextToken . parent ;
1222
+
1223
+ default :
1224
+ if ( isConstructorParameterCompletion ( contextToken ) ) {
1225
+ return contextToken . parent . parent as ConstructorDeclaration ;
1226
+ }
1227
+ }
1228
+ }
1229
+ return undefined ;
1230
+ }
1231
+
1178
1232
function tryGetContainingJsxElement ( contextToken : Node ) : JsxOpeningLikeElement {
1179
1233
if ( contextToken ) {
1180
1234
const parent = contextToken . parent ;
@@ -1250,11 +1304,14 @@ namespace ts.Completions {
1250
1304
containingNodeKind === SyntaxKind . VariableStatement ||
1251
1305
containingNodeKind === SyntaxKind . EnumDeclaration || // enum a { foo, |
1252
1306
isFunctionLikeButNotConstructor ( containingNodeKind ) ||
1253
- containingNodeKind === SyntaxKind . ClassDeclaration || // class A<T, |
1254
- containingNodeKind === SyntaxKind . ClassExpression || // var C = class D<T, |
1255
1307
containingNodeKind === SyntaxKind . InterfaceDeclaration || // interface A<T, |
1256
1308
containingNodeKind === SyntaxKind . ArrayBindingPattern || // var [x, y|
1257
- containingNodeKind === SyntaxKind . TypeAliasDeclaration ; // type Map, K, |
1309
+ containingNodeKind === SyntaxKind . TypeAliasDeclaration || // type Map, K, |
1310
+ // class A<T, |
1311
+ // var C = class D<T, |
1312
+ ( isClassLike ( contextToken . parent ) &&
1313
+ contextToken . parent . typeParameters &&
1314
+ contextToken . parent . typeParameters . end >= contextToken . pos ) ;
1258
1315
1259
1316
case SyntaxKind . DotToken :
1260
1317
return containingNodeKind === SyntaxKind . ArrayBindingPattern ; // var [.|
@@ -1298,7 +1355,7 @@ namespace ts.Completions {
1298
1355
case SyntaxKind . PublicKeyword :
1299
1356
case SyntaxKind . PrivateKeyword :
1300
1357
case SyntaxKind . ProtectedKeyword :
1301
- return containingNodeKind === SyntaxKind . Parameter ;
1358
+ return containingNodeKind === SyntaxKind . Parameter && ! isConstructorDeclaration ( contextToken . parent . parent ) ;
1302
1359
1303
1360
case SyntaxKind . AsKeyword :
1304
1361
return containingNodeKind === SyntaxKind . ImportSpecifier ||
@@ -1331,6 +1388,18 @@ namespace ts.Completions {
1331
1388
return false ;
1332
1389
}
1333
1390
1391
+ if ( isConstructorParameterCompletion ( contextToken ) ) {
1392
+ // constructor parameter completion is available only if
1393
+ // - its modifier of the constructor parameter or
1394
+ // - its name of the parameter and not being edited
1395
+ // eg. constructor(a |<- this shouldnt show completion
1396
+ if ( ! isIdentifier ( contextToken ) ||
1397
+ isConstructorParameterCompletionKeywordText ( contextToken . getText ( ) ) ||
1398
+ isCurrentlyEditingNode ( contextToken ) ) {
1399
+ return false ;
1400
+ }
1401
+ }
1402
+
1334
1403
// Previous token may have been a keyword that was converted to an identifier.
1335
1404
switch ( contextToken . getText ( ) ) {
1336
1405
case "abstract" :
@@ -1351,7 +1420,7 @@ namespace ts.Completions {
1351
1420
return true ;
1352
1421
}
1353
1422
1354
- return false ;
1423
+ return isDeclarationName ( contextToken ) && ! isJsxAttribute ( contextToken . parent ) ;
1355
1424
}
1356
1425
1357
1426
function isFunctionLikeButNotConstructor ( kind : SyntaxKind ) {
@@ -1574,14 +1643,45 @@ namespace ts.Completions {
1574
1643
}
1575
1644
1576
1645
// A cache of completion entries for keywords, these do not change between sessions
1577
- const keywordCompletions : CompletionEntry [ ] = [ ] ;
1578
- for ( let i = SyntaxKind . FirstKeyword ; i <= SyntaxKind . LastKeyword ; i ++ ) {
1579
- keywordCompletions . push ( {
1580
- name : tokenToString ( i ) ,
1581
- kind : ScriptElementKind . keyword ,
1582
- kindModifiers : ScriptElementKindModifier . none ,
1583
- sortText : "0"
1584
- } ) ;
1646
+ const _keywordCompletions : CompletionEntry [ ] [ ] = [ ] ;
1647
+ function getKeywordCompletions ( keywordFilter : KeywordCompletionFilters ) : CompletionEntry [ ] {
1648
+ const completions = _keywordCompletions [ keywordFilter ] ;
1649
+ if ( completions ) {
1650
+ return completions ;
1651
+ }
1652
+ return _keywordCompletions [ keywordFilter ] = generateKeywordCompletions ( keywordFilter ) ;
1653
+
1654
+ type FilterKeywordCompletions = ( entryName : string ) => boolean ;
1655
+ function generateKeywordCompletions ( keywordFilter : KeywordCompletionFilters ) {
1656
+ switch ( keywordFilter ) {
1657
+ case KeywordCompletionFilters . None :
1658
+ return getAllKeywordCompletions ( ) ;
1659
+ case KeywordCompletionFilters . ClassElementKeywords :
1660
+ return getFilteredKeywordCompletions ( isClassMemberCompletionKeywordText ) ;
1661
+ case KeywordCompletionFilters . ConstructorParameterKeywords :
1662
+ return getFilteredKeywordCompletions ( isConstructorParameterCompletionKeywordText ) ;
1663
+ }
1664
+ }
1665
+
1666
+ function getAllKeywordCompletions ( ) {
1667
+ const allKeywordsCompletions : CompletionEntry [ ] = [ ] ;
1668
+ for ( let i = SyntaxKind . FirstKeyword ; i <= SyntaxKind . LastKeyword ; i ++ ) {
1669
+ allKeywordsCompletions . push ( {
1670
+ name : tokenToString ( i ) ,
1671
+ kind : ScriptElementKind . keyword ,
1672
+ kindModifiers : ScriptElementKindModifier . none ,
1673
+ sortText : "0"
1674
+ } ) ;
1675
+ }
1676
+ return allKeywordsCompletions ;
1677
+ }
1678
+
1679
+ function getFilteredKeywordCompletions ( filterFn : FilterKeywordCompletions ) {
1680
+ return filter (
1681
+ getKeywordCompletions ( KeywordCompletionFilters . None ) ,
1682
+ entry => filterFn ( entry . name )
1683
+ ) ;
1684
+ }
1585
1685
}
1586
1686
1587
1687
function isClassMemberCompletionKeyword ( kind : SyntaxKind ) {
@@ -1604,8 +1704,19 @@ namespace ts.Completions {
1604
1704
return isClassMemberCompletionKeyword ( stringToToken ( text ) ) ;
1605
1705
}
1606
1706
1607
- const classMemberKeywordCompletions = filter ( keywordCompletions , entry =>
1608
- isClassMemberCompletionKeywordText ( entry . name ) ) ;
1707
+ function isConstructorParameterCompletionKeyword ( kind : SyntaxKind ) {
1708
+ switch ( kind ) {
1709
+ case SyntaxKind . PublicKeyword :
1710
+ case SyntaxKind . PrivateKeyword :
1711
+ case SyntaxKind . ProtectedKeyword :
1712
+ case SyntaxKind . ReadonlyKeyword :
1713
+ return true ;
1714
+ }
1715
+ }
1716
+
1717
+ function isConstructorParameterCompletionKeywordText ( text : string ) {
1718
+ return isConstructorParameterCompletionKeyword ( stringToToken ( text ) ) ;
1719
+ }
1609
1720
1610
1721
function isEqualityExpression ( node : Node ) : node is BinaryExpression {
1611
1722
return isBinaryExpression ( node ) && isEqualityOperatorKind ( node . operatorToken . kind ) ;
0 commit comments