Skip to content

Commit 9dd3ef4

Browse files
authored
Merge pull request #22849 from aozgaa/dev/aozgaa/cSharpObjLiteralFormatting
CSharp Style Object Literal Formatting
2 parents e149641 + 8c88ce7 commit 9dd3ef4

11 files changed

+206
-12
lines changed

src/services/formatting/formatting.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ namespace ts.formatting {
328328
break;
329329
}
330330

331-
if (SmartIndenter.shouldIndentChildNode(n, child)) {
331+
if (SmartIndenter.shouldIndentChildNode(options, n, child, sourceFile)) {
332332
return options.indentSize;
333333
}
334334

@@ -470,7 +470,7 @@ namespace ts.formatting {
470470
parentDynamicIndentation: DynamicIndentation,
471471
effectiveParentStartLine: number
472472
): { indentation: number, delta: number } {
473-
const delta = SmartIndenter.shouldIndentChildNode(node) ? options.indentSize : 0;
473+
const delta = SmartIndenter.shouldIndentChildNode(options, node) ? options.indentSize : 0;
474474

475475
if (effectiveParentStartLine === startLine) {
476476
// if node is located on the same line with the parent
@@ -514,7 +514,7 @@ namespace ts.formatting {
514514
if ((<MethodDeclaration>node).asteriskToken) {
515515
return SyntaxKind.AsteriskToken;
516516
}
517-
// falls through
517+
// falls through
518518
case SyntaxKind.PropertyDeclaration:
519519
case SyntaxKind.Parameter:
520520
return getNameOfDeclaration(<Declaration>node).kind;
@@ -541,9 +541,9 @@ namespace ts.formatting {
541541
getIndentation: () => indentation,
542542
getDelta,
543543
recomputeIndentation: lineAdded => {
544-
if (node.parent && SmartIndenter.shouldIndentChildNode(node.parent, node)) {
544+
if (node.parent && SmartIndenter.shouldIndentChildNode(options, node.parent, node, sourceFile)) {
545545
indentation += lineAdded ? options.indentSize : -options.indentSize;
546-
delta = SmartIndenter.shouldIndentChildNode(node) ? options.indentSize : 0;
546+
delta = SmartIndenter.shouldIndentChildNode(options, node) ? options.indentSize : 0;
547547
}
548548
}
549549
};
@@ -583,7 +583,7 @@ namespace ts.formatting {
583583

584584
function getDelta(child: TextRangeWithKind) {
585585
// Delta value should be zero when the node explicitly prevents indentation of the child node
586-
return SmartIndenter.nodeWillIndentChild(node, child, /*indentByDefault*/ true) ? delta : 0;
586+
return SmartIndenter.nodeWillIndentChild(options, node, child, sourceFile, /*indentByDefault*/ true) ? delta : 0;
587587
}
588588
}
589589

src/services/formatting/smartIndenter.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ namespace ts.formatting {
112112
let previous: Node | undefined;
113113
let current = precedingToken;
114114
while (current) {
115-
if (positionBelongsToNode(current, position, sourceFile) && shouldIndentChildNode(current, previous, /*isNextChild*/ true)) {
115+
if (positionBelongsToNode(current, position, sourceFile) && shouldIndentChildNode(options, current, previous, sourceFile, /*isNextChild*/ true)) {
116116
const currentStart = getStartLineAndCharacterForNode(current, sourceFile);
117117
const nextTokenKind = nextTokenIsCurlyBraceOnSameLineAsCursor(precedingToken, current, lineAtPosition, sourceFile);
118118
const indentationDelta = nextTokenKind !== NextTokenKind.Unknown
@@ -193,7 +193,7 @@ namespace ts.formatting {
193193
}
194194

195195
// increase indentation if parent node wants its content to be indented and parent and child nodes don't start on the same line
196-
if (shouldIndentChildNode(parent, current, isNextChild) && !parentAndChildShareLine) {
196+
if (shouldIndentChildNode(options, parent, current, sourceFile, isNextChild) && !parentAndChildShareLine) {
197197
indentationDelta += options.indentSize;
198198
}
199199

@@ -531,9 +531,17 @@ namespace ts.formatting {
531531
return false;
532532
}
533533

534-
export function nodeWillIndentChild(parent: TextRangeWithKind, child: TextRangeWithKind | undefined, indentByDefault: boolean): boolean {
534+
export function nodeWillIndentChild(settings: FormatCodeSettings | undefined, parent: TextRangeWithKind, child: TextRangeWithKind | undefined, sourceFile: SourceFileLike | undefined, indentByDefault: boolean): boolean {
535535
const childKind = child ? child.kind : SyntaxKind.Unknown;
536+
536537
switch (parent.kind) {
538+
case SyntaxKind.VariableDeclaration:
539+
case SyntaxKind.PropertyAssignment:
540+
case SyntaxKind.ObjectLiteralExpression:
541+
if (!settings.indentMultiLineObjectLiteralBeginningOnBlankLine && sourceFile && childKind === SyntaxKind.ObjectLiteralExpression) {
542+
return rangeIsOnOneLine(sourceFile, child);
543+
}
544+
break;
537545
case SyntaxKind.DoStatement:
538546
case SyntaxKind.WhileStatement:
539547
case SyntaxKind.ForInStatement:
@@ -585,9 +593,16 @@ namespace ts.formatting {
585593
* True when the parent node should indent the given child by an explicit rule.
586594
* @param isNextChild If true, we are judging indent of a hypothetical child *after* this one, not the current child.
587595
*/
588-
export function shouldIndentChildNode(parent: TextRangeWithKind, child?: TextRangeWithKind, isNextChild = false): boolean {
589-
return (nodeContentIsAlwaysIndented(parent.kind) || nodeWillIndentChild(parent, child, /*indentByDefault*/ false))
596+
export function shouldIndentChildNode(settings: FormatCodeSettings | undefined, parent: TextRangeWithKind, child?: Node, sourceFile?: SourceFileLike, isNextChild = false): boolean {
597+
return (nodeContentIsAlwaysIndented(parent.kind) || nodeWillIndentChild(settings, parent, child, sourceFile, /*indentByDefault*/ false))
590598
&& !(isNextChild && child && isControlFlowEndingStatement(child.kind, parent));
591599
}
600+
601+
function rangeIsOnOneLine(sourceFile: SourceFileLike, range: TextRangeWithKind) {
602+
const rangeStart = skipTrivia(sourceFile.text, range.pos);
603+
const startLine = sourceFile.getLineAndCharacterOfPosition(rangeStart).line;
604+
const endLine = sourceFile.getLineAndCharacterOfPosition(range.end).line;
605+
return startLine === endLine;
606+
}
592607
}
593608
}

src/services/textChanges.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -654,8 +654,9 @@ namespace ts.textChanges {
654654
? indentation
655655
: formatting.SmartIndenter.getIndentation(pos, sourceFile, formatOptions, prefix === newLineCharacter || getLineStartPositionForPosition(pos, sourceFile) === pos);
656656
if (delta === undefined) {
657-
delta = formatting.SmartIndenter.shouldIndentChildNode(nodeIn) ? (formatOptions.indentSize || 0) : 0;
657+
delta = formatting.SmartIndenter.shouldIndentChildNode(formatContext.options, nodeIn) ? (formatOptions.indentSize || 0) : 0;
658658
}
659+
659660
const file: SourceFileLike = { text, getLineAndCharacterOfPosition(pos) { return getLineAndCharacterOfPosition(this, pos); } };
660661
const changes = formatting.formatNodeGivenIndentation(node, file, sourceFile.languageVariant, initialIndentation, delta, formatContext);
661662
return applyChanges(text, changes);

src/services/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,7 @@ namespace ts {
630630
placeOpenBraceOnNewLineForFunctions?: boolean;
631631
placeOpenBraceOnNewLineForControlBlocks?: boolean;
632632
insertSpaceBeforeTypeAnnotation?: boolean;
633+
indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
633634
}
634635

635636
export interface DefinitionInfo {

tests/baselines/reference/api/tsserverlibrary.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4407,6 +4407,7 @@ declare namespace ts {
44074407
placeOpenBraceOnNewLineForFunctions?: boolean;
44084408
placeOpenBraceOnNewLineForControlBlocks?: boolean;
44094409
insertSpaceBeforeTypeAnnotation?: boolean;
4410+
indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
44104411
}
44114412
interface DefinitionInfo {
44124413
fileName: string;

tests/baselines/reference/api/typescript.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4660,6 +4660,7 @@ declare namespace ts {
46604660
placeOpenBraceOnNewLineForFunctions?: boolean;
46614661
placeOpenBraceOnNewLineForControlBlocks?: boolean;
46624662
insertSpaceBeforeTypeAnnotation?: boolean;
4663+
indentMultiLineObjectLiteralBeginningOnBlankLine?: boolean;
46634664
}
46644665
interface DefinitionInfo {
46654666
fileName: string;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
//// interface IPerson {
4+
//// coordinate: {
5+
//// x: number;
6+
//// y: number;
7+
//// }
8+
//// }
9+
//// class Person implements IPerson { }
10+
11+
verify.codeFix({
12+
description: "Implement interface 'IPerson'",
13+
newFileContent:
14+
`interface IPerson {
15+
coordinate: {
16+
x: number;
17+
y: number;
18+
}
19+
}
20+
class Person implements IPerson {
21+
coordinate: { x: number; y: number; };
22+
}`,
23+
});
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////
4+
//// namespace M {
5+
//// class C {
6+
//// foo() {
7+
//// /*a*/let x = {a:1};
8+
//// let y = {
9+
//// b: 2
10+
//// };
11+
//// let z =
12+
//// {
13+
//// c: 3
14+
//// };/*b*/
15+
//// return x.a + y.b + z.c;
16+
//// }
17+
//// }
18+
//// }
19+
////
20+
21+
goTo.select('a', 'b');
22+
edit.applyRefactor({
23+
refactorName: "Extract Symbol",
24+
actionName: "function_scope_1",
25+
actionDescription: "Extract to method in class 'C'",
26+
newContent:
27+
`
28+
namespace M {
29+
class C {
30+
foo() {
31+
let { x, y, z } = this./*RENAME*/newMethod();
32+
return x.a + y.b + z.c;
33+
}
34+
35+
private newMethod() {
36+
let x = { a: 1 };
37+
let y = {
38+
b: 2
39+
};
40+
let z = {
41+
c: 3
42+
};
43+
return { x, y, z };
44+
}
45+
}
46+
}
47+
`
48+
});
49+
50+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////
4+
//// var clear =
5+
//// {
6+
//// outerKey:
7+
//// {
8+
//// innerKey: 1,
9+
//// innerKey2:
10+
//// 2
11+
//// }
12+
//// };
13+
////
14+
15+
format.document();
16+
verify.currentFileContentIs(
17+
`
18+
var clear =
19+
{
20+
outerKey:
21+
{
22+
innerKey: 1,
23+
innerKey2:
24+
2
25+
}
26+
};
27+
`
28+
);
29+
30+
format.setOption("indentMultiLineObjectLiteralBeginningOnBlankLine", true);
31+
format.document();
32+
verify.currentFileContentIs(
33+
`
34+
var clear =
35+
{
36+
outerKey:
37+
{
38+
innerKey: 1,
39+
innerKey2:
40+
2
41+
}
42+
};
43+
`
44+
);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////
4+
//// var varName =/**/
5+
////
6+
7+
goTo.marker();
8+
edit.insert("\n{");
9+
verify.currentFileContentIs(
10+
`
11+
var varName =
12+
{
13+
`
14+
);
15+
16+
edit.insert("\na: 1");
17+
format.document();
18+
verify.currentFileContentIs(
19+
`
20+
var varName =
21+
{
22+
a: 1
23+
`
24+
);
25+
26+
edit.insert("\n};");
27+
28+
format.document();
29+
verify.currentFileContentIs(
30+
`
31+
var varName =
32+
{
33+
a: 1
34+
};
35+
`
36+
);

0 commit comments

Comments
 (0)