Skip to content

Commit 5c4c997

Browse files
committed
jsx opening element formatting
1 parent 7b77f6b commit 5c4c997

File tree

3 files changed

+48
-20
lines changed

3 files changed

+48
-20
lines changed

src/services/formatting/formatting.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -453,9 +453,9 @@ namespace ts.formatting {
453453
case SyntaxKind.MethodDeclaration:
454454
if ((<MethodDeclaration>node).asteriskToken) {
455455
return SyntaxKind.AsteriskToken;
456-
}
457-
// fall-through
458-
456+
}/*
457+
fall-through
458+
*/
459459
case SyntaxKind.PropertyDeclaration:
460460
case SyntaxKind.Parameter:
461461
return (<Declaration>node).name.kind;
@@ -729,7 +729,7 @@ namespace ts.formatting {
729729
else {
730730
// indent token only if end line of previous range does not match start line of the token
731731
const prevEndLine = savePreviousRange && sourceFile.getLineAndCharacterOfPosition(savePreviousRange.end).line;
732-
indentToken = lastTriviaWasNewLine && tokenStart.line !== prevEndLine;
732+
indentToken = lastTriviaWasNewLine && tokenStart.line !== prevEndLine;
733733
}
734734
}
735735
}
@@ -889,7 +889,7 @@ namespace ts.formatting {
889889
}
890890

891891
function indentationIsDifferent(indentationString: string, startLinePosition: number): boolean {
892-
return indentationString !== sourceFile.text.substr(startLinePosition , indentationString.length);
892+
return indentationString !== sourceFile.text.substr(startLinePosition, indentationString.length);
893893
}
894894

895895
function indentMultilineComment(commentRange: TextRange, indentation: number, firstLineIsIndented: boolean) {
@@ -933,7 +933,7 @@ namespace ts.formatting {
933933

934934
// shift all parts on the delta size
935935
const delta = indentation - nonWhitespaceColumnInFirstPart.column;
936-
for (let i = startIndex, len = parts.length; i < len; i++, startLine++) {
936+
for (let i = startIndex, len = parts.length; i < len; i++ , startLine++) {
937937
const startLinePos = getStartPositionOfLine(startLine, sourceFile);
938938
const nonWhitespaceCharacterAndColumn =
939939
i === 0

src/services/formatting/rules.ts

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ namespace ts.formatting {
231231
public NoSpaceBeforeCloseBraceInJsxExpression: Rule;
232232
public SpaceBeforeCloseBraceInJsxExpression: Rule;
233233

234+
// JSX opening elements
235+
public SpaceBeforeJsxAttribute: Rule;
236+
public SpaceBeforeSlashInJsxOpeningElement: Rule;
237+
public NoSpaceBeforeGreaterThanTokenInJsxOpeningElement: Rule;
238+
public NoSpaceBeforeEqualInJsxAttribute: Rule;
239+
public NoSpaceAfterEqualInJsxAttribute: Rule;
240+
234241
constructor() {
235242
///
236243
/// Common Rules
@@ -322,7 +329,7 @@ namespace ts.formatting {
322329

323330
// Add a space between statements. All keywords except (do,else,case) has open/close parens after them.
324331
// So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any]
325-
this.SpaceBetweenStatements = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isNonJsxElementContext, Rules.IsNotForContext), RuleAction.Space));
332+
this.SpaceBetweenStatements = new Rule(RuleDescriptor.create4(Shared.TokenRange.FromTokens([SyntaxKind.CloseParenToken, SyntaxKind.DoKeyword, SyntaxKind.ElseKeyword, SyntaxKind.CaseKeyword]), Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNotForContext), RuleAction.Space));
326333

327334
// This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter.
328335
this.SpaceAfterTryFinally = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword]), SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
@@ -386,6 +393,13 @@ namespace ts.formatting {
386393
// template string
387394
this.NoSpaceBetweenTagAndTemplateString = new Rule(RuleDescriptor.create3(SyntaxKind.Identifier, Shared.TokenRange.FromTokens([SyntaxKind.NoSubstitutionTemplateLiteral, SyntaxKind.TemplateHead])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
388395

396+
// jsx opening element
397+
this.SpaceBeforeJsxAttribute = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.Identifier), RuleOperation.create2(new RuleOperationContext(Rules.IsNextTokenParentJsxAttribute, Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
398+
this.SpaceBeforeSlashInJsxOpeningElement = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.SlashToken), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxSelfClosingElementContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
399+
this.NoSpaceBeforeGreaterThanTokenInJsxOpeningElement = new Rule(RuleDescriptor.create1(SyntaxKind.SlashToken, SyntaxKind.GreaterThanToken), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxSelfClosingElementContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
400+
this.NoSpaceBeforeEqualInJsxAttribute = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.EqualsToken), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxAttributeContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
401+
this.NoSpaceAfterEqualInJsxAttribute = new Rule(RuleDescriptor.create3(SyntaxKind.EqualsToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsJsxAttributeContext, Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
402+
389403
// These rules are higher in priority than user-configurable rules.
390404
this.HighPriorityCommonRules = [
391405
this.IgnoreBeforeComment, this.IgnoreAfterLineComment,
@@ -413,6 +427,8 @@ namespace ts.formatting {
413427
this.SpaceAfterVoidOperator,
414428
this.SpaceBetweenAsyncAndOpenParen, this.SpaceBetweenAsyncAndFunctionKeyword,
415429
this.NoSpaceBetweenTagAndTemplateString,
430+
this.SpaceBeforeJsxAttribute, this.SpaceBeforeSlashInJsxOpeningElement, this.NoSpaceBeforeGreaterThanTokenInJsxOpeningElement,
431+
this.NoSpaceBeforeEqualInJsxAttribute, this.NoSpaceAfterEqualInJsxAttribute,
416432

417433
// TypeScript-specific rules
418434
this.NoSpaceAfterConstructor, this.NoSpaceAfterModuleImport,
@@ -450,8 +466,8 @@ namespace ts.formatting {
450466
///
451467

452468
// Insert space after comma delimiter
453-
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
454-
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isNonJsxElementContext), RuleAction.Delete));
469+
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
470+
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNonJsxElementContext), RuleAction.Delete));
455471

456472
// Insert space before and after binary operators
457473
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
@@ -498,10 +514,10 @@ namespace ts.formatting {
498514
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
499515

500516
// No space after { and before } in JSX expression
501-
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Delete));
502-
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Space));
503-
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Delete));
504-
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.isJsxExpressionContext), RuleAction.Space));
517+
this.NoSpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
518+
this.SpaceAfterOpenBraceInJsxExpression = new Rule(RuleDescriptor.create3(SyntaxKind.OpenBraceToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
519+
this.NoSpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Delete));
520+
this.SpaceBeforeCloseBraceInJsxExpression = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsJsxExpressionContext), RuleAction.Space));
505521

506522
// Insert space after function keyword for anonymous functions
507523
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
@@ -741,14 +757,26 @@ namespace ts.formatting {
741757
return context.TokensAreOnSameLine() && context.contextNode.kind !== SyntaxKind.JsxText;
742758
}
743759

744-
static isNonJsxElementContext(context: FormattingContext): boolean {
760+
static IsNonJsxElementContext(context: FormattingContext): boolean {
745761
return context.contextNode.kind !== SyntaxKind.JsxElement;
746762
}
747763

748-
static isJsxExpressionContext(context: FormattingContext): boolean {
764+
static IsJsxExpressionContext(context: FormattingContext): boolean {
749765
return context.contextNode.kind === SyntaxKind.JsxExpression;
750766
}
751767

768+
static IsNextTokenParentJsxAttribute(context: FormattingContext): boolean {
769+
return context.nextTokenParent.kind === SyntaxKind.JsxAttribute;
770+
}
771+
772+
static IsJsxAttributeContext(context: FormattingContext): boolean {
773+
return context.contextNode.kind === SyntaxKind.JsxAttribute;
774+
}
775+
776+
static IsJsxSelfClosingElementContext(context: FormattingContext): boolean {
777+
return context.contextNode.kind === SyntaxKind.JsxSelfClosingElement;
778+
}
779+
752780
static IsNotBeforeBlockInFunctionDeclarationContext(context: FormattingContext): boolean {
753781
return !Rules.IsFunctionDeclContext(context) && !Rules.IsBeforeBlockContext(context);
754782
}

tests/cases/fourslash/formattingJsxElements.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@
7070
////<div>, {integer}</div>;/*commaInJsxElement2*/
7171
////<span>)</span>;/*closingParenInJsxElement*/
7272
////<span>) </span>;/*closingParenInJsxElement2*/
73-
////<Router routes={ 3 } />;/*jsxExpressionSpaces*/
73+
////<Router routes = { 3 } / >;/*jsxExpressionSpaces*/
7474
////<Router routes={ (3) } />;/*jsxExpressionSpaces2*/
7575

7676
format.document();
@@ -85,7 +85,7 @@ goTo.marker("indent1");
8585
verify.indentationIs(12);
8686

8787
goTo.marker("1");
88-
verify.currentLineContentIs(' class1= {');
88+
verify.currentLineContentIs(' class1={');
8989
goTo.marker("2");
9090
verify.currentLineContentIs(' }>');
9191

@@ -95,7 +95,7 @@ goTo.marker("indent2");
9595
verify.indentationIs(12);
9696

9797
goTo.marker("3");
98-
verify.currentLineContentIs(' class2= {');
98+
verify.currentLineContentIs(' class2={');
9999
goTo.marker("4");
100100
verify.currentLineContentIs(' }>');
101101

@@ -105,9 +105,9 @@ goTo.marker("indent3");
105105
verify.indentationIs(12);
106106

107107
goTo.marker("5");
108-
verify.currentLineContentIs(' class3= {');
108+
verify.currentLineContentIs(' class3={');
109109
goTo.marker("6");
110-
verify.currentLineContentIs(' }/>');
110+
verify.currentLineContentIs(' } />');
111111

112112

113113
goTo.marker("attrAutoformat");

0 commit comments

Comments
 (0)