@@ -3011,6 +3011,7 @@ namespace ts {
30113011
30123012 function tryGetGlobalSymbols ( ) : boolean {
30133013 let objectLikeContainer = tryGetObjectLikeCompletionContainer ( contextToken ) ;
3014+ let jsxContainer = tryGetContainingJsxElement ( contextToken ) ;
30143015 if ( objectLikeContainer ) {
30153016 // Object literal expression, look up possible property names from contextual type
30163017 isMemberCompletion = true ;
@@ -3061,30 +3062,19 @@ namespace ts {
30613062 }
30623063 return true ;
30633064 }
3064- else if ( getAncestor ( contextToken , SyntaxKind . JsxElement ) || getAncestor ( contextToken , SyntaxKind . JsxSelfClosingElement ) ) {
3065- // Go up until we hit either the element or expression
3066- let jsxNode = contextToken ;
3065+ else if ( jsxContainer ) {
3066+ let attrsType : Type ;
3067+ if ( ( jsxContainer . kind === SyntaxKind . JsxSelfClosingElement ) || ( jsxContainer . kind === SyntaxKind . JsxOpeningElement ) ) {
3068+ // Cursor is inside a JSX self-closing element or opening element
3069+ attrsType = typeChecker . getJsxElementAttributesType ( < JsxOpeningLikeElement > jsxContainer ) ;
30673070
3068- while ( jsxNode ) {
3069- if ( jsxNode . kind === SyntaxKind . JsxExpression ) {
3070- // Defer to global completion if we're inside an {expression}
3071- break ;
3072- } else if ( jsxNode . kind === SyntaxKind . JsxSelfClosingElement || jsxNode . kind === SyntaxKind . JsxElement ) {
3073- let attrsType : Type ;
3074- if ( jsxNode . kind === SyntaxKind . JsxSelfClosingElement ) {
3075- // Cursor is inside a JSX self-closing element
3076- attrsType = typeChecker . getJsxElementAttributesType ( < JsxSelfClosingElement > jsxNode ) ;
3077- }
3078- else {
3079- Debug . assert ( jsxNode . kind === SyntaxKind . JsxElement ) ;
3080- // Cursor is inside a JSX element
3081- attrsType = typeChecker . getJsxElementAttributesType ( ( < JsxElement > jsxNode ) . openingElement ) ;
3082- }
3083- symbols = typeChecker . getPropertiesOfType ( attrsType ) ;
3071+ if ( attrsType ) {
3072+ symbols = filterJsxAttributes ( ( < JsxOpeningLikeElement > jsxContainer ) . attributes , typeChecker . getPropertiesOfType ( attrsType ) ) ;
30843073 isMemberCompletion = true ;
3074+ isNewIdentifierLocation = false ;
30853075 return true ;
30863076 }
3087- jsxNode = jsxNode . parent ;
3077+
30883078 }
30893079 }
30903080
@@ -3270,6 +3260,36 @@ namespace ts {
32703260 return undefined ;
32713261 }
32723262
3263+ function tryGetContainingJsxElement ( contextToken : Node ) : JsxOpeningLikeElement {
3264+ if ( contextToken ) {
3265+ let parent = contextToken . parent ;
3266+ switch ( contextToken . kind ) {
3267+ case SyntaxKind . LessThanSlashToken :
3268+ case SyntaxKind . SlashToken :
3269+ case SyntaxKind . Identifier :
3270+ if ( parent && ( parent . kind === SyntaxKind . JsxSelfClosingElement || parent . kind === SyntaxKind . JsxOpeningElement ) ) {
3271+ return < JsxOpeningLikeElement > parent ;
3272+ }
3273+ break ;
3274+
3275+ case SyntaxKind . CloseBraceToken :
3276+ // The context token is the closing } of an attribute, which means
3277+ // its parent is a JsxExpression, whose parent is a JsxAttribute,
3278+ // whose parent is a JsxOpeningLikeElement
3279+ if ( parent &&
3280+ parent . kind === SyntaxKind . JsxExpression &&
3281+ parent . parent &&
3282+ parent . parent . kind === SyntaxKind . JsxAttribute ) {
3283+
3284+ return < JsxOpeningLikeElement > parent . parent . parent ;
3285+ }
3286+
3287+ break ;
3288+ }
3289+ }
3290+ return undefined ;
3291+ }
3292+
32733293 function isFunction ( kind : SyntaxKind ) : boolean {
32743294 switch ( kind ) {
32753295 case SyntaxKind . FunctionExpression :
@@ -3455,6 +3475,22 @@ namespace ts {
34553475 }
34563476 }
34573477
3478+ function filterJsxAttributes ( attributes : NodeArray < JsxAttribute | JsxSpreadAttribute > , symbols : Symbol [ ] ) : Symbol [ ] {
3479+ let seenNames : Map < boolean > = { } ;
3480+ for ( let attr of attributes ) {
3481+ if ( attr . kind === SyntaxKind . JsxAttribute ) {
3482+ seenNames [ ( < JsxAttribute > attr ) . name . text ] = true ;
3483+ }
3484+ }
3485+ let result : Symbol [ ] = [ ] ;
3486+ for ( let sym of symbols ) {
3487+ if ( ! seenNames [ sym . name ] ) {
3488+ result . push ( sym ) ;
3489+ }
3490+ }
3491+ return result ;
3492+ }
3493+
34583494 function getCompletionsAtPosition ( fileName : string , position : number ) : CompletionInfo {
34593495 synchronizeHostData ( ) ;
34603496
0 commit comments