Skip to content

CSharp Style Object Literal Formatting #22849

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 9 commits into from
Mar 30, 2018

Conversation

aozgaa
Copy link
Contributor

@aozgaa aozgaa commented Mar 23, 2018

Fixes a vsfeedback issue: https://developercommunity.visualstudio.com/content/problem/217993/javascript-braces-incorrectly-alignedindented.html

Today, we format object literals that star on their own line like this

var clear =
    {
        outerKey:
            {
                innerKey: 1
            }
    };

the user asked that we instead format them like this

var clear =
{
    outerKey:
    {
        innerKey: 1
    }
};

which is what this PR implements.

One subtlety is in the handling of formatting a synthesized object literal node. Since it is synthesized, the node doesn't exist within a sourcefile. So, we cannot reference the node's sourcefile to figure out if it is multiline (where the non-indentation of the open curly kicks in). Today, all object literals are synthesized to be single line, so the multi-line check just defers to the default indentation.

This reasoning coudld be made more explicit as a comment within the smart indenter if a reviewer considers it worthwhile.

@aozgaa aozgaa requested review from billti, uniqueiniquity, amcasey and a user March 23, 2018 23:08
if (childKind === SyntaxKind.ObjectLiteralExpression) {
const sourceFile = child.getSourceFile();
if (sourceFile) {
// May not be defined for synthesized nodes.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was meant to describe the if (sourceFile)? I think it should go on the same line then.

const sourceFile = child.getSourceFile();
if (sourceFile) {
// May not be defined for synthesized nodes.
const startLine = sourceFile.getLineAndCharacterOfPosition(child.getStart()).line;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use an isSingleLineNode(Node, SourceFile) helper

// May not be defined for synthesized nodes.
const startLine = sourceFile.getLineAndCharacterOfPosition(child.getStart()).line;
const endLine = sourceFile.getLineAndCharacterOfPosition(child.getEnd()).line;
return startLine === endLine;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did any of the tests cover the case where this is false?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added test where the startline is the same as the endline, despite leading trivia making the object-literal child's pos inappropriate to refer to.

Copy link
Member

@amcasey amcasey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any back compat concerns?

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Mar 25, 2018

I have back compat concerns. I'd sort of rather this be an option for users than the default. At the time of writing, the suggestion has 0 upvotes, so I have no idea who would want this feature vs. who would be very unhappy with this feature.

case SyntaxKind.PropertyAssignment:
case SyntaxKind.ObjectLiteralExpression:
if (childKind === SyntaxKind.ObjectLiteralExpression) {
const sourceFile = child.getSourceFile();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass in the sourceFile instead of walking up on every node.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and keep child TextRangeWithKind

@mhegazy
Copy link
Contributor

mhegazy commented Mar 26, 2018

We do need an option to control this formatting change. does not matter much which is the default really, but a way to opt out/in into this

@aozgaa
Copy link
Contributor Author

aozgaa commented Mar 26, 2018

@mhegazy @DanielRosenwasser To clarify, this doesn't actually put object literals on their own line. All it does is change the indentation of object literals that already started on their own line and are multi-line.

Should we still offer a switch?

@mhegazy
Copy link
Contributor

mhegazy commented Mar 26, 2018

Well, we need to have formatting working correctly as well... I am assuming ppl want to format the file and have the formatting sticks..

it would be weird to have something like:

var x =
{
    a: 0,
    b: "",
    |
}

work, but then you add a semi-colon after the } and the whole thing moves 4 spaces to the right.

case SyntaxKind.ObjectLiteralExpression:
if (sourceFile && childKind === SyntaxKind.ObjectLiteralExpression) {
const childStart = skipTrivia(sourceFile.text, child.pos);
const startLine = sourceFile.getLineAndCharacterOfPosition(childStart).line;
Copy link

@ghost ghost Mar 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My previous comment's marked outdated since this code was moved: This could use an isSingleLineNode(Node, SourceFile) helper function.

@@ -581,9 +581,9 @@ namespace ts.formatting {
&& !(node.decorators && kind === getFirstNonDecoratorTokenOfNode(node));
}

function getDelta(child: TextRangeWithKind) {
function getDelta(child: Node) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not think you need this change from TextRangeWithKind to Node.

@aozgaa aozgaa merged commit 9dd3ef4 into microsoft:master Mar 30, 2018
@microsoft microsoft locked and limited conversation to collaborators Jul 25, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants