From f465d99492db1d328a42a9b93c1e955cdaa540b3 Mon Sep 17 00:00:00 2001 From: Ryan Cavanaugh Date: Wed, 1 Jul 2015 15:00:06 -0700 Subject: [PATCH] Fix attribute completion following JSX exprs --- src/services/services.ts | 69 ++++++++++++------- .../incrementalParsingInsertIntoMethod1.ts | 1 - ...yntacticClassificationsConflictMarkers1.ts | 2 +- tests/cases/fourslash/tsxCompletion3.ts | 13 ++++ 4 files changed, 60 insertions(+), 25 deletions(-) create mode 100644 tests/cases/fourslash/tsxCompletion3.ts diff --git a/src/services/services.ts b/src/services/services.ts index 018a11fc4bb..c193b53582b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -3009,6 +3009,7 @@ namespace ts { function tryGetGlobalSymbols(): boolean { let objectLikeContainer = tryGetObjectLikeCompletionContainer(contextToken); + let jsxContainer = tryGetContainingJsxElement(contextToken); if (objectLikeContainer) { // Object literal expression, look up possible property names from contextual type isMemberCompletion = true; @@ -3059,30 +3060,22 @@ namespace ts { } return true; } - else if (getAncestor(contextToken, SyntaxKind.JsxElement) || getAncestor(contextToken, SyntaxKind.JsxSelfClosingElement)) { - // Go up until we hit either the element or expression - let jsxNode = contextToken; + else if(jsxContainer) { + let attrsType: Type; + if (jsxContainer.kind === SyntaxKind.JsxSelfClosingElement) { + // Cursor is inside a JSX self-closing element + attrsType = typeChecker.getJsxElementAttributesType(jsxContainer); + } + else if(jsxContainer.kind === SyntaxKind.JsxOpeningElement) { + // Cursor is inside a JSX element + attrsType = typeChecker.getJsxElementAttributesType(jsxContainer); + } - while (jsxNode) { - if (jsxNode.kind === SyntaxKind.JsxExpression) { - // Defer to global completion if we're inside an {expression} - break; - } else if (jsxNode.kind === SyntaxKind.JsxSelfClosingElement || jsxNode.kind === SyntaxKind.JsxElement) { - let attrsType: Type; - if (jsxNode.kind === SyntaxKind.JsxSelfClosingElement) { - // Cursor is inside a JSX self-closing element - attrsType = typeChecker.getJsxElementAttributesType(jsxNode); - } - else { - Debug.assert(jsxNode.kind === SyntaxKind.JsxElement); - // Cursor is inside a JSX element - attrsType = typeChecker.getJsxElementAttributesType((jsxNode).openingElement); - } - symbols = typeChecker.getPropertiesOfType(attrsType); - isMemberCompletion = true; - return true; - } - jsxNode = jsxNode.parent; + if (attrsType) { + symbols = typeChecker.getPropertiesOfType(attrsType); + isMemberCompletion = true; + isNewIdentifierLocation = false; + return true; } } @@ -3268,6 +3261,36 @@ namespace ts { return undefined; } + function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement { + if (contextToken) { + let parent = contextToken.parent; + switch(contextToken.kind) { + case SyntaxKind.LessThanSlashToken: + case SyntaxKind.SlashToken: + case SyntaxKind.Identifier: + if(parent && (parent.kind === SyntaxKind.JsxSelfClosingElement || parent.kind === SyntaxKind.JsxOpeningElement)) { + return parent; + } + break; + + case SyntaxKind.CloseBraceToken: + // The context token is the closing } of an attribute, which means + // its parent is a JsxExpression, whose parent is a JsxAttribute, + // whose parent is a JsxOpeningLikeElement + if(parent && + parent.kind === SyntaxKind.JsxExpression && + parent.parent && + parent.parent.kind === SyntaxKind.JsxAttribute) { + + return parent.parent.parent; + } + + break; + } + } + return undefined; + } + function isFunction(kind: SyntaxKind): boolean { switch (kind) { case SyntaxKind.FunctionExpression: diff --git a/tests/cases/fourslash/incrementalParsingInsertIntoMethod1.ts b/tests/cases/fourslash/incrementalParsingInsertIntoMethod1.ts index 930c0511ffb..b5deb361056 100644 --- a/tests/cases/fourslash/incrementalParsingInsertIntoMethod1.ts +++ b/tests/cases/fourslash/incrementalParsingInsertIntoMethod1.ts @@ -8,6 +8,5 @@ //// public foo3() { } ////} -debugger; goTo.marker("1"); edit.insert(" + 1"); \ No newline at end of file diff --git a/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts b/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts index e381856c41e..7f26038c33e 100644 --- a/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts +++ b/tests/cases/fourslash/syntacticClassificationsConflictMarkers1.ts @@ -7,7 +7,7 @@ //// v = 2; ////>>>>>>> Branch - a ////} -debugger; + var c = classification; verify.syntacticClassificationsAre( c.keyword("class"), c.className("C"), c.punctuation("{"), diff --git a/tests/cases/fourslash/tsxCompletion3.ts b/tests/cases/fourslash/tsxCompletion3.ts new file mode 100644 index 00000000000..2d87d7ed056 --- /dev/null +++ b/tests/cases/fourslash/tsxCompletion3.ts @@ -0,0 +1,13 @@ +/// + +//@Filename: file.tsx +//// declare module JSX { +//// interface Element { } +//// interface IntrinsicElements { +//// div: { one; two; } +//// } +//// } +////
; + +goTo.marker(); +verify.completionListContains('two');