Skip to content

Commit ec02077

Browse files
authored
Merge pull request #9361 from SaschaNaz/noFormatInJsxElement
Fix formatting on JsxElement/JsxExpression
2 parents a91978e + 6e984eb commit ec02077

File tree

7 files changed

+93
-9
lines changed

7 files changed

+93
-9
lines changed

src/harness/fourslash.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,7 @@ namespace FourSlash {
324324
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
325325
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
326326
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
327+
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
327328
PlaceOpenBraceOnNewLineForFunctions: false,
328329
PlaceOpenBraceOnNewLineForControlBlocks: false,
329330
};

src/server/editorServices.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,6 +1580,7 @@ namespace ts.server {
15801580
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
15811581
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
15821582
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
1583+
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
15831584
PlaceOpenBraceOnNewLineForFunctions: false,
15841585
PlaceOpenBraceOnNewLineForControlBlocks: false,
15851586
});

src/services/formatting/rules.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,12 @@ namespace ts.formatting {
225225
public NoSpaceBeforeTemplateMiddleAndTail: Rule;
226226
public SpaceBeforeTemplateMiddleAndTail: Rule;
227227

228+
// No space after { and before } in JSX expression
229+
public NoSpaceAfterOpenBraceInJsxExpression: Rule;
230+
public SpaceAfterOpenBraceInJsxExpression: Rule;
231+
public NoSpaceBeforeCloseBraceInJsxExpression: Rule;
232+
public SpaceBeforeCloseBraceInJsxExpression: Rule;
233+
228234
constructor() {
229235
///
230236
/// Common Rules
@@ -316,7 +322,7 @@ namespace ts.formatting {
316322

317323
// Add a space between statements. All keywords except (do,else,case) has open/close parens after them.
318324
// So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any]
319-
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.IsNotForContext), RuleAction.Space));
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));
320326

321327
// This low-pri rule takes care of "try {" and "finally {" in case the rule SpaceBeforeOpenBraceInControl didn't execute on FormatOnEnter.
322328
this.SpaceAfterTryFinally = new Rule(RuleDescriptor.create2(Shared.TokenRange.FromTokens([SyntaxKind.TryKeyword, SyntaxKind.FinallyKeyword]), SyntaxKind.OpenBraceToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
@@ -444,8 +450,8 @@ namespace ts.formatting {
444450
///
445451

446452
// Insert space after comma delimiter
447-
this.SpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsNextTokenNotCloseBracket), RuleAction.Space));
448-
this.NoSpaceAfterComma = new Rule(RuleDescriptor.create3(SyntaxKind.CommaToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
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));
449455

450456
// Insert space before and after binary operators
451457
this.SpaceBeforeBinaryOperator = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.BinaryOperators), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext, Rules.IsBinaryOpContext), RuleAction.Space));
@@ -491,6 +497,12 @@ namespace ts.formatting {
491497
this.NoSpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
492498
this.SpaceBeforeTemplateMiddleAndTail = new Rule(RuleDescriptor.create4(Shared.TokenRange.Any, Shared.TokenRange.FromTokens([SyntaxKind.TemplateMiddle, SyntaxKind.TemplateTail])), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
493499

500+
// 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));
505+
494506
// Insert space after function keyword for anonymous functions
495507
this.SpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Space));
496508
this.NoSpaceAfterAnonymousFunctionKeyword = new Rule(RuleDescriptor.create1(SyntaxKind.FunctionKeyword, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsFunctionDeclContext), RuleAction.Delete));
@@ -729,6 +741,14 @@ namespace ts.formatting {
729741
return context.TokensAreOnSameLine() && context.contextNode.kind !== SyntaxKind.JsxText;
730742
}
731743

744+
static isNonJsxElementContext(context: FormattingContext): boolean {
745+
return context.contextNode.kind !== SyntaxKind.JsxElement;
746+
}
747+
748+
static isJsxExpressionContext(context: FormattingContext): boolean {
749+
return context.contextNode.kind === SyntaxKind.JsxExpression;
750+
}
751+
732752
static IsNotBeforeBlockInFunctionDeclarationContext(context: FormattingContext): boolean {
733753
return !Rules.IsFunctionDeclContext(context) && !Rules.IsBeforeBlockContext(context);
734754
}

src/services/formatting/rulesProvider.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ namespace ts.formatting {
9090
rules.push(this.globalRules.NoSpaceBeforeTemplateMiddleAndTail);
9191
}
9292

93+
if (options.InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces) {
94+
rules.push(this.globalRules.SpaceAfterOpenBraceInJsxExpression);
95+
rules.push(this.globalRules.SpaceBeforeCloseBraceInJsxExpression);
96+
}
97+
else {
98+
rules.push(this.globalRules.NoSpaceAfterOpenBraceInJsxExpression);
99+
rules.push(this.globalRules.NoSpaceBeforeCloseBraceInJsxExpression);
100+
}
101+
93102
if (options.InsertSpaceAfterSemicolonInForStatements) {
94103
rules.push(this.globalRules.SpaceAfterSemicolonInFor);
95104
}

src/services/services.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,7 @@ namespace ts {
12671267
InsertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: boolean;
12681268
InsertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: boolean;
12691269
InsertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: boolean;
1270+
InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
12701271
PlaceOpenBraceOnNewLineForFunctions: boolean;
12711272
PlaceOpenBraceOnNewLineForControlBlocks: boolean;
12721273
[s: string]: boolean | number | string | undefined;

tests/cases/fourslash/formattingJsxElements.ts

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//// </div>
1010
//// )
1111
////}
12-
////
12+
////
1313
////function foo1() {
1414
//// return (
1515
//// <div className="commentBox" data-id="test">
@@ -45,8 +45,8 @@
4545
//// class3= {/*5*/
4646
//// }/>/*6*/
4747
//// )
48-
////}
49-
////
48+
////}
49+
////
5050
////(function () {
5151
//// return <div
5252
////className=""/*attrAutoformat*/
@@ -64,7 +64,14 @@
6464
/////*childJsxElementIndent*/
6565
////<span></span>/*grandchildJsxElementAutoformat*/
6666
////</span>/*containedClosingTagAutoformat*/
67-
////</h5>
67+
////</h5>;
68+
////
69+
////<div>,{integer}</div>;/*commaInJsxElement*/
70+
////<div>, {integer}</div>;/*commaInJsxElement2*/
71+
////<span>)</span>;/*closingParenInJsxElement*/
72+
////<span>) </span>;/*closingParenInJsxElement2*/
73+
////<Router routes={ 3 } />;/*jsxExpressionSpaces*/
74+
////<Router routes={ (3) } />;/*jsxExpressionSpaces2*/
6875

6976
format.document();
7077
goTo.marker("autoformat");
@@ -114,7 +121,7 @@ verify.indentationIs(12);
114121

115122
goTo.marker("danglingBracketAutoformat")
116123
// TODO: verify.currentLineContentIs(" >");
117-
verify.currentLineContentIs(" >");
124+
verify.currentLineContentIs(" >");
118125
goTo.marker("closingTagAutoformat");
119126
verify.currentLineContentIs(" </div>");
120127

@@ -125,4 +132,17 @@ verify.indentationIs(8);
125132
goTo.marker("grandchildJsxElementAutoformat");
126133
verify.currentLineContentIs(" <span></span>");
127134
goTo.marker("containedClosingTagAutoformat");
128-
verify.currentLineContentIs(" </span>");
135+
verify.currentLineContentIs(" </span>");
136+
137+
goTo.marker("commaInJsxElement");
138+
verify.currentLineContentIs("<div>,{integer}</div>;");
139+
goTo.marker("commaInJsxElement2");
140+
verify.currentLineContentIs("<div>, {integer}</div>;");
141+
goTo.marker("closingParenInJsxElement");
142+
verify.currentLineContentIs("<span>)</span>;");
143+
goTo.marker("closingParenInJsxElement2");
144+
verify.currentLineContentIs("<span>) </span>;");
145+
goTo.marker("jsxExpressionSpaces");
146+
verify.currentLineContentIs("<Router routes={3} />;");
147+
goTo.marker("jsxExpressionSpaces2");
148+
verify.currentLineContentIs("<Router routes={(3)} />;");
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
///<reference path="fourslash.ts"/>
2+
3+
//@Filename: file.tsx
4+
/////*InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces*/<Madoka homu={ true } saya={ (true) } />;
5+
6+
runTest("InsertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces", "<Madoka homu={ true } saya={ (true) } />;", "<Madoka homu={true} saya={(true)} />;");
7+
8+
9+
function runTest(propertyName: string, expectedStringWhenTrue: string, expectedStringWhenFalse: string) {
10+
// Go to the correct file
11+
goTo.marker(propertyName);
12+
13+
// Set the option to false first
14+
format.setOption(propertyName, false);
15+
16+
// Format
17+
format.document();
18+
19+
// Verify
20+
goTo.marker(propertyName);
21+
verify.currentLineContentIs(expectedStringWhenFalse);
22+
23+
// Set the option to true
24+
format.setOption(propertyName, true);
25+
26+
// Format
27+
format.document();
28+
29+
// Verify
30+
goTo.marker(propertyName);
31+
verify.currentLineContentIs(expectedStringWhenTrue);
32+
}

0 commit comments

Comments
 (0)