diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 30626a83d6..435acf548b 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -3285,6 +3285,30 @@ namespace FourSlashInterface { return getClassification("typeAliasName", text, position); } + export function jsxOpenTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: FourSlash.TextSpan } { + return getClassification("jsxOpenTagName", text, position); + } + + export function jsxCloseTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: FourSlash.TextSpan } { + return getClassification("jsxCloseTagName", text, position); + } + + export function jsxSelfClosingTagName(text: string, position?: number): { classificationType: string; text: string; textSpan?: FourSlash.TextSpan } { + return getClassification("jsxSelfClosingTagName", text, position); + } + + export function jsxAttribute(text: string, position?: number): { classificationType: string; text: string; textSpan?: FourSlash.TextSpan } { + return getClassification("jsxAttribute", text, position); + } + + export function jsxText(text: string, position?: number): { classificationType: string; text: string; textSpan?: FourSlash.TextSpan } { + return getClassification("jsxText", text, position); + } + + export function jsxAttributeStringLiteralValue(text: string, position?: number): { classificationType: string; text: string; textSpan?: FourSlash.TextSpan } { + return getClassification("jsxAttributeStringLiteralValue", text, position); + } + function getClassification(type: string, text: string, position?: number) { return { classificationType: type, diff --git a/src/services/services.ts b/src/services/services.ts index 52f6e12d39..0086519f90 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1616,6 +1616,9 @@ namespace ts { public static jsxOpenTagName = "jsx open tag name"; public static jsxCloseTagName = "jsx close tag name"; public static jsxSelfClosingTagName = "jsx self closing tag name"; + public static jsxAttribute = "jsx attribute"; + public static jsxText = "jsx text"; + public static jsxAttributeStringLiteralValue = "jsx attribute string literal value"; } export const enum ClassificationType { @@ -1640,7 +1643,9 @@ namespace ts { jsxOpenTagName = 19, jsxCloseTagName = 20, jsxSelfClosingTagName = 21, - jsxAttribute = 22 + jsxAttribute = 22, + jsxText = 23, + jsxAttributeStringLiteralValue = 24, } /// Language Service @@ -6595,6 +6600,9 @@ namespace ts { case ClassificationType.jsxOpenTagName: return ClassificationTypeNames.jsxOpenTagName; case ClassificationType.jsxCloseTagName: return ClassificationTypeNames.jsxCloseTagName; case ClassificationType.jsxSelfClosingTagName: return ClassificationTypeNames.jsxSelfClosingTagName; + case ClassificationType.jsxAttribute: return ClassificationTypeNames.jsxAttribute; + case ClassificationType.jsxText: return ClassificationTypeNames.jsxText; + case ClassificationType.jsxAttributeStringLiteralValue: return ClassificationTypeNames.jsxAttributeStringLiteralValue; } } @@ -6803,12 +6811,12 @@ namespace ts { } } - function classifyToken(token: Node): void { + function classifyTokenOrJsxText(token: Node): void { if (nodeIsMissing(token)) { return; } - const tokenStart = classifyLeadingTriviaAndGetTokenStart(token); + const tokenStart = token.kind === SyntaxKind.JsxText ? token.pos : classifyLeadingTriviaAndGetTokenStart(token); const tokenWidth = token.end - tokenStart; Debug.assert(tokenWidth >= 0); @@ -6844,7 +6852,8 @@ namespace ts { // the '=' in a variable declaration is special cased here. if (token.parent.kind === SyntaxKind.VariableDeclaration || token.parent.kind === SyntaxKind.PropertyDeclaration || - token.parent.kind === SyntaxKind.Parameter) { + token.parent.kind === SyntaxKind.Parameter || + token.parent.kind === SyntaxKind.JsxAttribute) { return ClassificationType.operator; } } @@ -6863,7 +6872,7 @@ namespace ts { return ClassificationType.numericLiteral; } else if (tokenKind === SyntaxKind.StringLiteral || tokenKind === SyntaxKind.StringLiteralType) { - return ClassificationType.stringLiteral; + return token.parent.kind === SyntaxKind.JsxAttribute ? ClassificationType.jsxAttributeStringLiteralValue : ClassificationType.stringLiteral; } else if (tokenKind === SyntaxKind.RegularExpressionLiteral) { // TODO: we should get another classification type for these literals. @@ -6873,6 +6882,9 @@ namespace ts { // TODO (drosen): we should *also* get another classification type for these literals. return ClassificationType.stringLiteral; } + else if (tokenKind === SyntaxKind.JsxText) { + return ClassificationType.jsxText; + } else if (tokenKind === SyntaxKind.Identifier) { if (token) { switch (token.parent.kind) { @@ -6946,8 +6958,8 @@ namespace ts { const children = element.getChildren(sourceFile); for (let i = 0, n = children.length; i < n; i++) { const child = children[i]; - if (isToken(child)) { - classifyToken(child); + if (isToken(child) || child.kind === SyntaxKind.JsxText) { + classifyTokenOrJsxText(child); } else { // Recurse into our child nodes. diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 0e83189dd8..b69a757f01 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -338,6 +338,36 @@ declare namespace FourSlashInterface { text: string; textSpan?: TextSpan; }; + function jsxOpenTagName(text: string, position?: number): { + classificationType: string; + text: string; + textSpan?: TextSpan; + }; + function jsxCloseTagName(text: string, position?: number): { + classificationType: string; + text: string; + textSpan?: TextSpan; + }; + function jsxSelfClosingTagName(text: string, position?: number): { + classificationType: string; + text: string; + textSpan?: TextSpan; + }; + function jsxAttribute(text: string, position?: number): { + classificationType: string; + text: string; + textSpan?: TextSpan; + }; + function jsxText(text: string, position?: number): { + classificationType: string; + text: string; + textSpan?: TextSpan; + }; + function jsxAttributeStringLiteralValue(text: string, position?: number): { + classificationType: string; + text: string; + textSpan?: TextSpan; + }; } } declare function verifyOperationIsCancelled(f: any): void; diff --git a/tests/cases/fourslash/syntacticClassificationsJsx1.ts b/tests/cases/fourslash/syntacticClassificationsJsx1.ts new file mode 100644 index 0000000000..e9de07c759 --- /dev/null +++ b/tests/cases/fourslash/syntacticClassificationsJsx1.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"), + c.jsxAttribute("a"), c.operator("="), c.jsxAttributeStringLiteralValue(`"some-value"`), + c.jsxAttribute("b"), c.operator("="), c.punctuation("{"), c.numericLiteral("1"), c.punctuation("}"), + c.punctuation(">"), + c.jsxText(` + some jsx text +`), + c.punctuation("<"), c.punctuation("/"), c.jsxCloseTagName("div"), c.punctuation(">"), c.punctuation(";"), + c.keyword("let"), c.identifier("y"), c.operator("="), + c.punctuation("<"), + c.jsxSelfClosingTagName("element"), + c.jsxAttribute("attr"), c.operator("="), c.jsxAttributeStringLiteralValue(`"123"`), + c.punctuation("/"), c.punctuation(">") +) \ No newline at end of file