Skip to content

add support for classifying jsx text and string literals in jsx attributes #6411

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jan 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions src/harness/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
26 changes: 19 additions & 7 deletions src/services/services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -1640,7 +1643,9 @@ namespace ts {
jsxOpenTagName = 19,
jsxCloseTagName = 20,
jsxSelfClosingTagName = 21,
jsxAttribute = 22
jsxAttribute = 22,
jsxText = 23,
jsxAttributeStringLiteralValue = 24,
}

/// Language Service
Expand Down Expand Up @@ -6575,6 +6580,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;
}
}

Expand Down Expand Up @@ -6783,12 +6791,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);
Expand Down Expand Up @@ -6824,7 +6832,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;
}
}
Expand All @@ -6843,7 +6852,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.
Expand All @@ -6853,6 +6862,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) {
Expand Down Expand Up @@ -6926,8 +6938,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.
Expand Down
30 changes: 30 additions & 0 deletions tests/cases/fourslash/fourslash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions tests/cases/fourslash/syntacticClassificationsJsx1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// <reference path="fourslash.ts"/>

// @Filename: file1.tsx
////let x = <div a = "some-value" b = {1}>
//// some jsx text
////</div>;
////
////let y = <element attr="123"/>

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(">")
)