@@ -18,7 +18,8 @@ namespace ts.Completions {
18
18
19
19
const enum KeywordCompletionFilters {
20
20
None ,
21
- ClassElementKeywords , // Keywords at class keyword
21
+ ClassElementKeywords , // Keywords inside class body
22
+ InterfaceElementKeywords , // Keywords inside interface body
22
23
ConstructorParameterKeywords , // Keywords at constructor parameter
23
24
FunctionLikeBodyKeywords , // Keywords at function like body
24
25
TypeKeywords ,
@@ -1527,58 +1528,51 @@ namespace ts.Completions {
1527
1528
* Relevant symbols are stored in the captured 'symbols' variable.
1528
1529
*/
1529
1530
function tryGetClassLikeCompletionSymbols ( ) : GlobalsSearch {
1530
- const classLikeDeclaration = tryGetClassLikeCompletionContainer ( contextToken ) ;
1531
- if ( ! classLikeDeclaration ) return GlobalsSearch . Continue ;
1531
+ const decl = tryGetObjectTypeDeclarationCompletionContainer ( sourceFile , contextToken , location ) ;
1532
+ if ( ! decl ) return GlobalsSearch . Continue ;
1532
1533
1533
1534
// We're looking up possible property names from parent type.
1534
1535
completionKind = CompletionKind . MemberLike ;
1535
1536
// Declaring new property/method/accessor
1536
1537
isNewIdentifierLocation = true ;
1537
1538
// Has keywords for class elements
1538
- keywordFilters = KeywordCompletionFilters . ClassElementKeywords ;
1539
+ keywordFilters = isClassLike ( decl ) ? KeywordCompletionFilters . ClassElementKeywords : KeywordCompletionFilters . InterfaceElementKeywords ;
1539
1540
1540
- const baseTypeNode = getClassExtendsHeritageClauseElement ( classLikeDeclaration ) ;
1541
- const implementsTypeNodes = getClassImplementsHeritageClauseElements ( classLikeDeclaration ) ;
1542
- if ( baseTypeNode || implementsTypeNodes ) {
1543
- const classElement = contextToken . parent ;
1544
- let classElementModifierFlags = isClassElement ( classElement ) && getModifierFlags ( classElement ) ;
1541
+ // If you're in an interface you don't want to repeat things from super-interface. So just stop here.
1542
+ if ( ! isClassLike ( decl ) ) return GlobalsSearch . Success ;
1543
+
1544
+ const baseTypeNode = getClassExtendsHeritageClauseElement ( decl ) ;
1545
+ const implementsTypeNodes = getClassImplementsHeritageClauseElements ( decl ) ;
1546
+ if ( ! baseTypeNode && ! implementsTypeNodes ) return GlobalsSearch . Success ;
1547
+
1548
+ const classElement = contextToken . parent ;
1549
+ const classElementModifierFlags = ( isClassElement ( classElement ) ? getModifierFlags ( classElement ) : ModifierFlags . None )
1545
1550
// If this is context token is not something we are editing now, consider if this would lead to be modifier
1546
- if ( contextToken . kind === SyntaxKind . Identifier && ! isCurrentlyEditingNode ( contextToken ) ) {
1547
- switch ( contextToken . getText ( ) ) {
1548
- case "private" :
1549
- classElementModifierFlags = classElementModifierFlags | ModifierFlags . Private ;
1550
- break ;
1551
- case "static" :
1552
- classElementModifierFlags = classElementModifierFlags | ModifierFlags . Static ;
1553
- break ;
1554
- }
1555
- }
1551
+ | ( isIdentifier ( contextToken ) && ! isCurrentlyEditingNode ( contextToken ) ? modifierToFlag ( contextToken . originalKeywordKind ) : ModifierFlags . None ) ;
1556
1552
1557
- // No member list for private methods
1558
- if ( ! ( classElementModifierFlags & ModifierFlags . Private ) ) {
1559
- let baseClassTypeToGetPropertiesFrom : Type ;
1560
- if ( baseTypeNode ) {
1561
- baseClassTypeToGetPropertiesFrom = typeChecker . getTypeAtLocation ( baseTypeNode ) ;
1562
- if ( classElementModifierFlags & ModifierFlags . Static ) {
1563
- // Use static class to get property symbols from
1564
- baseClassTypeToGetPropertiesFrom = typeChecker . getTypeOfSymbolAtLocation (
1565
- baseClassTypeToGetPropertiesFrom . symbol , classLikeDeclaration ) ;
1566
- }
1567
- }
1568
- const implementedInterfaceTypePropertySymbols = ( classElementModifierFlags & ModifierFlags . Static ) ?
1569
- emptyArray :
1570
- flatMap ( implementsTypeNodes || emptyArray , typeNode => typeChecker . getPropertiesOfType ( typeChecker . getTypeAtLocation ( typeNode ) ) ) ;
1571
-
1572
- // List of property symbols of base type that are not private and already implemented
1573
- symbols = filterClassMembersList (
1574
- baseClassTypeToGetPropertiesFrom ?
1575
- typeChecker . getPropertiesOfType ( baseClassTypeToGetPropertiesFrom ) :
1576
- emptyArray ,
1577
- implementedInterfaceTypePropertySymbols ,
1578
- classLikeDeclaration . members ,
1579
- classElementModifierFlags ) ;
1553
+ // No member list for private methods
1554
+ if ( classElementModifierFlags & ModifierFlags . Private ) return GlobalsSearch . Success ;
1555
+
1556
+ let baseClassTypeToGetPropertiesFrom : Type | undefined ;
1557
+ if ( baseTypeNode ) {
1558
+ baseClassTypeToGetPropertiesFrom = typeChecker . getTypeAtLocation ( baseTypeNode ) ;
1559
+ if ( classElementModifierFlags & ModifierFlags . Static ) {
1560
+ // Use static class to get property symbols from
1561
+ baseClassTypeToGetPropertiesFrom = typeChecker . getTypeOfSymbolAtLocation ( baseClassTypeToGetPropertiesFrom . symbol , decl ) ;
1580
1562
}
1581
1563
}
1564
+
1565
+ const implementedInterfaceTypePropertySymbols = ! implementsTypeNodes || ( classElementModifierFlags & ModifierFlags . Static )
1566
+ ? emptyArray
1567
+ : flatMap ( implementsTypeNodes , typeNode => typeChecker . getPropertiesOfType ( typeChecker . getTypeAtLocation ( typeNode ) ) ) ;
1568
+
1569
+ // List of property symbols of base type that are not private and already implemented
1570
+ symbols = filterClassMembersList (
1571
+ baseClassTypeToGetPropertiesFrom ? typeChecker . getPropertiesOfType ( baseClassTypeToGetPropertiesFrom ) : emptyArray ,
1572
+ implementedInterfaceTypePropertySymbols ,
1573
+ decl . members ,
1574
+ classElementModifierFlags ) ;
1575
+
1582
1576
return GlobalsSearch . Success ;
1583
1577
}
1584
1578
@@ -1622,10 +1616,6 @@ namespace ts.Completions {
1622
1616
return undefined ;
1623
1617
}
1624
1618
1625
- function isFromClassElementDeclaration ( node : Node ) {
1626
- return node . parent && isClassElement ( node . parent ) && isClassLike ( node . parent . parent ) ;
1627
- }
1628
-
1629
1619
function isParameterOfConstructorDeclaration ( node : Node ) {
1630
1620
return isParameter ( node ) && isConstructorDeclaration ( node . parent ) ;
1631
1621
}
@@ -1636,56 +1626,6 @@ namespace ts.Completions {
1636
1626
( isConstructorParameterCompletionKeyword ( node . kind ) || isDeclarationName ( node ) ) ;
1637
1627
}
1638
1628
1639
- /**
1640
- * Returns the immediate owning class declaration of a context token,
1641
- * on the condition that one exists and that the context implies completion should be given.
1642
- */
1643
- function tryGetClassLikeCompletionContainer ( contextToken : Node ) : ClassLikeDeclaration {
1644
- if ( contextToken ) {
1645
- switch ( contextToken . kind ) {
1646
- case SyntaxKind . OpenBraceToken : // class c { |
1647
- if ( isClassLike ( contextToken . parent ) ) {
1648
- return contextToken . parent ;
1649
- }
1650
- break ;
1651
-
1652
- // class c {getValue(): number, | }
1653
- case SyntaxKind . CommaToken :
1654
- if ( isClassLike ( contextToken . parent ) ) {
1655
- return contextToken . parent ;
1656
- }
1657
- break ;
1658
-
1659
- // class c {getValue(): number; | }
1660
- case SyntaxKind . SemicolonToken :
1661
- // class c { method() { } | }
1662
- case SyntaxKind . CloseBraceToken :
1663
- if ( isClassLike ( location ) ) {
1664
- return location ;
1665
- }
1666
- // class c { method() { } b| }
1667
- if ( isFromClassElementDeclaration ( location ) &&
1668
- ( location . parent as ClassElement ) . name === location ) {
1669
- return location . parent . parent as ClassLikeDeclaration ;
1670
- }
1671
- break ;
1672
-
1673
- default :
1674
- if ( isFromClassElementDeclaration ( contextToken ) &&
1675
- ( isClassMemberCompletionKeyword ( contextToken . kind ) ||
1676
- isClassMemberCompletionKeywordText ( contextToken . getText ( ) ) ) ) {
1677
- return contextToken . parent . parent as ClassLikeDeclaration ;
1678
- }
1679
- }
1680
- }
1681
-
1682
- // class c { method() { } | method2() { } }
1683
- if ( location && location . kind === SyntaxKind . SyntaxList && isClassLike ( location . parent ) ) {
1684
- return location . parent ;
1685
- }
1686
- return undefined ;
1687
- }
1688
-
1689
1629
/**
1690
1630
* Returns the immediate owning class declaration of a context token,
1691
1631
* on the condition that one exists and that the context implies completion should be given.
@@ -1820,15 +1760,7 @@ namespace ts.Completions {
1820
1760
isFunctionLikeButNotConstructor ( containingNodeKind ) ;
1821
1761
1822
1762
case SyntaxKind . OpenBraceToken :
1823
- return containingNodeKind === SyntaxKind . EnumDeclaration || // enum a { |
1824
- containingNodeKind === SyntaxKind . InterfaceDeclaration || // interface a { |
1825
- containingNodeKind === SyntaxKind . TypeLiteral ; // const x : { |
1826
-
1827
- case SyntaxKind . SemicolonToken :
1828
- return containingNodeKind === SyntaxKind . PropertySignature &&
1829
- contextToken . parent && contextToken . parent . parent &&
1830
- ( contextToken . parent . parent . kind === SyntaxKind . InterfaceDeclaration || // interface a { f; |
1831
- contextToken . parent . parent . kind === SyntaxKind . TypeLiteral ) ; // const x : { a; |
1763
+ return containingNodeKind === SyntaxKind . EnumDeclaration ; // enum a { |
1832
1764
1833
1765
case SyntaxKind . LessThanToken :
1834
1766
return containingNodeKind === SyntaxKind . ClassDeclaration || // class A< |
@@ -1857,7 +1789,7 @@ namespace ts.Completions {
1857
1789
1858
1790
case SyntaxKind . GetKeyword :
1859
1791
case SyntaxKind . SetKeyword :
1860
- if ( isFromClassElementDeclaration ( contextToken ) ) {
1792
+ if ( isFromObjectTypeDeclaration ( contextToken ) ) {
1861
1793
return false ;
1862
1794
}
1863
1795
// falls through
@@ -1877,7 +1809,7 @@ namespace ts.Completions {
1877
1809
// If the previous token is keyword correspoding to class member completion keyword
1878
1810
// there will be completion available here
1879
1811
if ( isClassMemberCompletionKeywordText ( contextToken . getText ( ) ) &&
1880
- isFromClassElementDeclaration ( contextToken ) ) {
1812
+ isFromObjectTypeDeclaration ( contextToken ) ) {
1881
1813
return false ;
1882
1814
}
1883
1815
@@ -2162,6 +2094,8 @@ namespace ts.Completions {
2162
2094
return kind !== SyntaxKind . UndefinedKeyword ;
2163
2095
case KeywordCompletionFilters . ClassElementKeywords :
2164
2096
return isClassMemberCompletionKeyword ( kind ) ;
2097
+ case KeywordCompletionFilters . InterfaceElementKeywords :
2098
+ return isInterfaceOrTypeLiteralCompletionKeyword ( kind ) ;
2165
2099
case KeywordCompletionFilters . ConstructorParameterKeywords :
2166
2100
return isConstructorParameterCompletionKeyword ( kind ) ;
2167
2101
case KeywordCompletionFilters . FunctionLikeBodyKeywords :
@@ -2174,6 +2108,10 @@ namespace ts.Completions {
2174
2108
} ) ) ;
2175
2109
}
2176
2110
2111
+ function isInterfaceOrTypeLiteralCompletionKeyword ( kind : SyntaxKind ) : boolean {
2112
+ return kind === SyntaxKind . ReadonlyKeyword ;
2113
+ }
2114
+
2177
2115
function isClassMemberCompletionKeyword ( kind : SyntaxKind ) {
2178
2116
switch ( kind ) {
2179
2117
case SyntaxKind . PublicKeyword :
@@ -2282,4 +2220,44 @@ namespace ts.Completions {
2282
2220
! ( memberType . flags & TypeFlags . Primitive || checker . isArrayLikeType ( memberType ) || typeHasCallOrConstructSignatures ( memberType , checker ) ) ) ;
2283
2221
return Debug . assertEachDefined ( checker . getAllPossiblePropertiesOfTypes ( filteredTypes ) , "getAllPossiblePropertiesOfTypes() should all be defined" ) ;
2284
2222
}
2223
+
2224
+ /**
2225
+ * Returns the immediate owning class declaration of a context token,
2226
+ * on the condition that one exists and that the context implies completion should be given.
2227
+ */
2228
+ function tryGetObjectTypeDeclarationCompletionContainer ( sourceFile : SourceFile , contextToken : Node | undefined , location : Node ) : ObjectTypeDeclaration | undefined {
2229
+ // class c { method() { } | method2() { } }
2230
+ switch ( location . kind ) {
2231
+ case SyntaxKind . SyntaxList :
2232
+ return tryCast ( location . parent , isObjectTypeDeclaration ) ;
2233
+ case SyntaxKind . EndOfFileToken :
2234
+ const cls = tryCast ( lastOrUndefined ( cast ( location . parent , isSourceFile ) . statements ) , isObjectTypeDeclaration ) ;
2235
+ if ( cls && ! findChildOfKind ( cls , SyntaxKind . CloseBraceToken , sourceFile ) ) {
2236
+ return cls ;
2237
+ }
2238
+ }
2239
+
2240
+ if ( ! contextToken ) return undefined ;
2241
+ switch ( contextToken . kind ) {
2242
+ case SyntaxKind . SemicolonToken : // class c {getValue(): number; | }
2243
+ case SyntaxKind . CloseBraceToken : // class c { method() { } | }
2244
+ // class c { method() { } b| }
2245
+ return isFromObjectTypeDeclaration ( location ) && ( location . parent as ClassElement | TypeElement ) . name === location
2246
+ ? location . parent . parent as ObjectTypeDeclaration
2247
+ : tryCast ( location , isObjectTypeDeclaration ) ;
2248
+ case SyntaxKind . OpenBraceToken : // class c { |
2249
+ case SyntaxKind . CommaToken : // class c {getValue(): number, | }
2250
+ return tryCast ( contextToken . parent , isObjectTypeDeclaration ) ;
2251
+ default :
2252
+ if ( ! isFromObjectTypeDeclaration ( contextToken ) ) return undefined ;
2253
+ const isValidKeyword = isClassLike ( contextToken . parent . parent ) ? isClassMemberCompletionKeyword : isInterfaceOrTypeLiteralCompletionKeyword ;
2254
+ return ( isValidKeyword ( contextToken . kind ) || isIdentifier ( contextToken ) && isValidKeyword ( stringToToken ( contextToken . text ) ) )
2255
+ ? contextToken . parent . parent as ObjectTypeDeclaration : undefined ;
2256
+ }
2257
+ }
2258
+
2259
+ // TODO: GH#19856 Would like to return `node is Node & { parent: (ClassElement | TypeElement) & { parent: ObjectTypeDeclaration } }` but then compilation takes > 10 minutes
2260
+ function isFromObjectTypeDeclaration ( node : Node ) : boolean {
2261
+ return node . parent && ( isClassElement ( node . parent ) || isTypeElement ( node . parent ) ) && isObjectTypeDeclaration ( node . parent . parent ) ;
2262
+ }
2285
2263
}
0 commit comments