diff --git a/src/services/services.ts b/src/services/services.ts index 37f54de5c3c..0016a27acc3 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -6856,21 +6856,58 @@ namespace ts { } } - function classifyTokenOrJsxText(token: Node): void { - if (nodeIsMissing(token)) { - return; + /** + * Returns true if node should be treated as classified and no further processing is required. + * False will mean that node is not classified and traverse routine should recurse into node contents. + */ + function tryClassifyNode(node: Node): boolean { + if (nodeIsMissing(node)) { + return true; } - const tokenStart = token.kind === SyntaxKind.JsxText ? token.pos : classifyLeadingTriviaAndGetTokenStart(token); + const classifiedElementName = tryClassifyJsxElementName(node); + if (!isToken(node) && node.kind !== SyntaxKind.JsxText && classifiedElementName === undefined) { + return false; + } - const tokenWidth = token.end - tokenStart; + const tokenStart = node.kind === SyntaxKind.JsxText ? node.pos : classifyLeadingTriviaAndGetTokenStart(node); + + const tokenWidth = node.end - tokenStart; Debug.assert(tokenWidth >= 0); if (tokenWidth > 0) { - const type = classifyTokenType(token.kind, token); + const type = classifiedElementName || classifyTokenType(node.kind, node); if (type) { pushClassification(tokenStart, tokenWidth, type); } } + + return true; + } + + function tryClassifyJsxElementName(token: Node): ClassificationType { + switch (token.parent && token.parent.kind) { + case SyntaxKind.JsxOpeningElement: + if ((token.parent).tagName === token) { + return ClassificationType.jsxOpenTagName; + } + break; + case SyntaxKind.JsxClosingElement: + if ((token.parent).tagName === token) { + return ClassificationType.jsxCloseTagName; + } + break; + case SyntaxKind.JsxSelfClosingElement: + if ((token.parent).tagName === token) { + return ClassificationType.jsxSelfClosingTagName; + } + break; + case SyntaxKind.JsxAttribute: + if ((token.parent).name === token) { + return ClassificationType.jsxAttribute; + } + break; + } + return undefined; } // for accurate classification, the actual token should be passed in. however, for @@ -6963,28 +7000,6 @@ namespace ts { return ClassificationType.parameterName; } return; - - case SyntaxKind.JsxOpeningElement: - if ((token.parent).tagName === token) { - return ClassificationType.jsxOpenTagName; - } - return; - - case SyntaxKind.JsxClosingElement: - if ((token.parent).tagName === token) { - return ClassificationType.jsxCloseTagName; - } - return; - - case SyntaxKind.JsxSelfClosingElement: - if ((token.parent).tagName === token) { - return ClassificationType.jsxSelfClosingTagName; - } - return; - case SyntaxKind.JsxAttribute: - if ((token.parent).name === token) { - return ClassificationType.jsxAttribute; - } } } return ClassificationType.identifier; @@ -7003,10 +7018,7 @@ namespace ts { const children = element.getChildren(sourceFile); for (let i = 0, n = children.length; i < n; i++) { const child = children[i]; - if (isToken(child) || child.kind === SyntaxKind.JsxText) { - classifyTokenOrJsxText(child); - } - else { + if (!tryClassifyNode(child)) { // Recurse into our child nodes. processElement(child); } diff --git a/tests/cases/fourslash/syntacticClassificationsJsx2.ts b/tests/cases/fourslash/syntacticClassificationsJsx2.ts new file mode 100644 index 00000000000..9110c6ce4b9 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsJsx2.ts @@ -0,0 +1,27 @@ +/// + +// @Filename: file1.tsx +////let x = +//// some jsx text +////; +//// +////let y = + +const c = classification; +verify.syntacticClassificationsAre( + c.keyword("let"), c.identifier("x"), c.operator("="), + c.punctuation("<"), + c.jsxOpenTagName("div.name"), + c.jsxAttribute("b"), c.operator("="), c.jsxAttributeStringLiteralValue(`"some-value"`), + c.jsxAttribute("c"), c.operator("="), c.punctuation("{"), c.numericLiteral("1"), c.punctuation("}"), + c.punctuation(">"), + c.jsxText(` + some jsx text +`), + c.punctuation("<"), c.punctuation("/"), c.jsxCloseTagName("div.name"), c.punctuation(">"), c.punctuation(";"), + c.keyword("let"), c.identifier("y"), c.operator("="), + c.punctuation("<"), + c.jsxSelfClosingTagName("element.name"), + c.jsxAttribute("attr"), c.operator("="), c.jsxAttributeStringLiteralValue(`"123"`), + c.punctuation("/"), c.punctuation(">") +) \ No newline at end of file