@@ -18,7 +18,8 @@ namespace ts.Completions {
1818
1919 const enum KeywordCompletionFilters {
2020 None ,
21- ClassElementKeywords , // Keywords at class keyword
21+ ClassElementKeywords , // Keywords inside class body
22+ InterfaceElementKeywords , // Keywords inside interface body
2223 ConstructorParameterKeywords , // Keywords at constructor parameter
2324 FunctionLikeBodyKeywords , // Keywords at function like body
2425 TypeKeywords ,
@@ -1527,58 +1528,51 @@ namespace ts.Completions {
15271528 * Relevant symbols are stored in the captured 'symbols' variable.
15281529 */
15291530 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 ;
15321533
15331534 // We're looking up possible property names from parent type.
15341535 completionKind = CompletionKind . MemberLike ;
15351536 // Declaring new property/method/accessor
15361537 isNewIdentifierLocation = true ;
15371538 // Has keywords for class elements
1538- keywordFilters = KeywordCompletionFilters . ClassElementKeywords ;
1539+ keywordFilters = isClassLike ( decl ) ? KeywordCompletionFilters . ClassElementKeywords : KeywordCompletionFilters . InterfaceElementKeywords ;
15391540
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 )
15451550 // 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 ) ;
15561552
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 ) ;
15801562 }
15811563 }
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+
15821576 return GlobalsSearch . Success ;
15831577 }
15841578
@@ -1622,10 +1616,6 @@ namespace ts.Completions {
16221616 return undefined ;
16231617 }
16241618
1625- function isFromClassElementDeclaration ( node : Node ) {
1626- return node . parent && isClassElement ( node . parent ) && isClassLike ( node . parent . parent ) ;
1627- }
1628-
16291619 function isParameterOfConstructorDeclaration ( node : Node ) {
16301620 return isParameter ( node ) && isConstructorDeclaration ( node . parent ) ;
16311621 }
@@ -1636,56 +1626,6 @@ namespace ts.Completions {
16361626 ( isConstructorParameterCompletionKeyword ( node . kind ) || isDeclarationName ( node ) ) ;
16371627 }
16381628
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-
16891629 /**
16901630 * Returns the immediate owning class declaration of a context token,
16911631 * on the condition that one exists and that the context implies completion should be given.
@@ -1820,15 +1760,7 @@ namespace ts.Completions {
18201760 isFunctionLikeButNotConstructor ( containingNodeKind ) ;
18211761
18221762 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 { |
18321764
18331765 case SyntaxKind . LessThanToken :
18341766 return containingNodeKind === SyntaxKind . ClassDeclaration || // class A< |
@@ -1857,7 +1789,7 @@ namespace ts.Completions {
18571789
18581790 case SyntaxKind . GetKeyword :
18591791 case SyntaxKind . SetKeyword :
1860- if ( isFromClassElementDeclaration ( contextToken ) ) {
1792+ if ( isFromObjectTypeDeclaration ( contextToken ) ) {
18611793 return false ;
18621794 }
18631795 // falls through
@@ -1877,7 +1809,7 @@ namespace ts.Completions {
18771809 // If the previous token is keyword correspoding to class member completion keyword
18781810 // there will be completion available here
18791811 if ( isClassMemberCompletionKeywordText ( contextToken . getText ( ) ) &&
1880- isFromClassElementDeclaration ( contextToken ) ) {
1812+ isFromObjectTypeDeclaration ( contextToken ) ) {
18811813 return false ;
18821814 }
18831815
@@ -2162,6 +2094,8 @@ namespace ts.Completions {
21622094 return kind !== SyntaxKind . UndefinedKeyword ;
21632095 case KeywordCompletionFilters . ClassElementKeywords :
21642096 return isClassMemberCompletionKeyword ( kind ) ;
2097+ case KeywordCompletionFilters . InterfaceElementKeywords :
2098+ return isInterfaceOrTypeLiteralCompletionKeyword ( kind ) ;
21652099 case KeywordCompletionFilters . ConstructorParameterKeywords :
21662100 return isConstructorParameterCompletionKeyword ( kind ) ;
21672101 case KeywordCompletionFilters . FunctionLikeBodyKeywords :
@@ -2174,6 +2108,10 @@ namespace ts.Completions {
21742108 } ) ) ;
21752109 }
21762110
2111+ function isInterfaceOrTypeLiteralCompletionKeyword ( kind : SyntaxKind ) : boolean {
2112+ return kind === SyntaxKind . ReadonlyKeyword ;
2113+ }
2114+
21772115 function isClassMemberCompletionKeyword ( kind : SyntaxKind ) {
21782116 switch ( kind ) {
21792117 case SyntaxKind . PublicKeyword :
@@ -2282,4 +2220,44 @@ namespace ts.Completions {
22822220 ! ( memberType . flags & TypeFlags . Primitive || checker . isArrayLikeType ( memberType ) || typeHasCallOrConstructSignatures ( memberType , checker ) ) ) ;
22832221 return Debug . assertEachDefined ( checker . getAllPossiblePropertiesOfTypes ( filteredTypes ) , "getAllPossiblePropertiesOfTypes() should all be defined" ) ;
22842222 }
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+ }
22852263}
0 commit comments