Skip to content
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

[Fix] jsx-indent with tabs #1058

Merged
merged 1 commit into from Feb 16, 2017

Conversation

kentcdodds
Copy link
Contributor

@kentcdodds kentcdodds commented Feb 1, 2017

This fixes the --fix for jsx-indent. Specifically my issue was with tabs (Fixes #1057), but this fixes the same issues with spaces as well.

Highlevel important bits:

  1. Adds fix for when the ending of a tag (>) is on its own line. This will now lint and fix properly.
  2. Adds fix for linting and fixing Literal strings inside JSX
  3. Adds configuration option called indentLogicalExpressions which will allow for indenting the right side of logical expressions (documented)
  4. Adds fix for linting and fixing the indentation of the closing tag.

@kentcdodds kentcdodds force-pushed the pr/jsx-indent-tabs-fix branch 12 times, most recently from 1e7fbe5 to d809e34 Compare February 1, 2017 20:17
Copy link
Contributor Author

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

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

I think I'm ready for a solid review on this PR. I have verified that these changes fix the repo referenced in #1057


if (loc) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Based on the ESLint docs, if loc isn't provided, it defaults to node.loc so I figured we could just do the same and save ourselves an if statement :)


var parentElementIndent = getNodeIndent(prevToken);
if (prevToken.type === 'JSXElement') {
parentElementIndent = getOpeningElementIndent(prevToken.openingElement);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note the recurssion here.

// as the baseline for the next two, instead of the realizing the entire three
// lines are wrong together. See #608
/* output: [
output: [
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Woot! These changes fix this case and we can now auto-fix this!

@@ -212,17 +214,6 @@ ruleTester.run('jsx-indent', rule, {
].join('\n'),
parserOptions: parserOptions
}, {
// Literals indentation is not touched
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 think that Literal indentation should be touched as it doesn't make a difference in the resulting HTML (different for JSXExpressions, but that doesn't apply here).

parserOptions: parserOptions
// }, {
// should we put effort in making this work?
// who in their right mind would do this?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Question stands... It would require more logic in the fixer and I don't think it's worth the complexity.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this is worth supporting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, I went ahead and removed the commented-out code.

@kentcdodds
Copy link
Contributor Author

Pinging those who have touched this rule in one way or another: @yannickcr, @Jorundur, @eelyafi, @jayphelps, @petersendidit, @voxpelli

Thanks friends!

@kentcdodds
Copy link
Contributor Author

Don't merge this yet, just found a bug with the following code:

<div>
  {
    foo &&
      <div>
        <div></div>
      </div>
  }
</div>

We're getting a bad report on the middle-most div. Looking into fixing that.

@kentcdodds
Copy link
Contributor Author

Just noticed that this is actually by design (a few valid tests showing this). I'm going to add a config option to enable this style.

@kentcdodds kentcdodds force-pushed the pr/jsx-indent-tabs-fix branch 2 times, most recently from a226f40 to 83396d3 Compare February 1, 2017 21:31
@kentcdodds
Copy link
Contributor Author

Ok, I've updated the PR with documentation for the new configuration option indentLogicalExpressions

@kentcdodds
Copy link
Contributor Author

Oh, and just an argument for the indentLogicalExpressions pattern is the language-babel plugin for Atom (761,282 downloads) has an option to auto-indent JSX and this is how it indents things. Also I personally prefer this indentation style FWIW :)

@kentcdodds
Copy link
Contributor Author

I published my branch as @kentcdodds/eslint-plugin-react if anyone wants to try it out.

@kentcdodds
Copy link
Contributor Author

I've verified that this works on my work project 👍

@jayphelps
Copy link
Contributor

Is this multiple fixes to separate indention cases?

@kentcdodds
Copy link
Contributor Author

Is this multiple fixes to separate indention cases?

<ng-sheepishly>Yes, it is...</ng-sheepishly>

Originally, it was to fix a single issue with --fix, but I noticed that in my project we also indent multi-line LogicalExpressions so I slipped that in. I'd definitely prefer to just merge things together (as they are now), but can create a separate PR if people really feel strongly about this...

@jayphelps
Copy link
Contributor

jayphelps commented Feb 1, 2017

It's just a bit hard for me to follow with the various changes for separate things. I don't have enough cycles to fully understand it so I can't give a sign off, but it generally looks good. There were a couple changes you made that are opinionated--you called them out with comments.

@kentcdodds
Copy link
Contributor Author

Understood @jayphelps, if others feel this way, then I'll split things up.

@jayphelps
Copy link
Contributor

Could you perhaps edit the original post to include a summary of the changed behaviors?

parentElementIndent = getOpeningElementIndent(prevToken.openingElement);
}

if (isRightInLogicalExp(node) && indentLogicalExpressions) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the only relevant bit for the indentLogicalExpressions option.

* @param {ASTNode} node The JSXOpeningElement
* @return {Number} the number of indentation characters it should have
*/
function getOpeningElementIndent(node) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This function came from the JSXOpeningElement function below. I extracted it to make use of it else where. The only changes from what was there before is the two if statements before the var indent = ( assignment.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for providing this context. This is helpful for reviewers.

@kentcdodds
Copy link
Contributor Author

Updated, let me know if more details would be helpful. Thanks @jayphelps!

return {
JSXOpeningElement: function(node) {
var prevToken = sourceCode.getTokenBefore(node);
if (!prevToken) {
return;
}
// Use the parent in a list or an array
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This code was simply refactored to getOpeningElementIndent

Copy link
Collaborator

@lencioni lencioni left a comment

Choose a reason for hiding this comment

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

This looks great to me. Thanks for the contribution @kentcdodds.

@yannickcr it would be nice to get this in the next release.

@@ -165,8 +184,9 @@ module.exports = {
} while (token.type === 'JSXText' && /^\s*$/.test(token.value));
var startLine = node.loc.start.line;
var endLine = token ? token.loc.end.line : -1;
var whitespaceOnly = token ? /\n\s*$/.test(token.value) : false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

This probably doesn't matter, but I think this regex won't match the first line? Should it be /^\s*$/ instead? I'm also not sure what token.value represents, so perhaps the \n is necessary. (see also the regex on line 184)

If these end up being the same, it might not be a bad idea to move them to a constant at the module level.

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 just tried out changing it to ^ and it results in an additional extraneous error in one of the test cases. Specifically:

function MyComponent(props) {
	return (
    <div
			className="foo-bar"
			id="thing"
    >
      Hello world!
    </div>
	)
}

Because token.value is actually everything between the > and the </div> (which includes a \n)

* @param {ASTNode} node The JSXOpeningElement
* @return {Number} the number of indentation characters it should have
*/
function getOpeningElementIndent(node) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for providing this context. This is helpful for reviewers.

if (prevToken.type === 'JSXText' || prevToken.type === 'Punctuator' && prevToken.value === ',') {
prevToken = sourceCode.getNodeByRangeIndex(prevToken.start);
prevToken = prevToken.type === 'Literal' ? prevToken.parent : prevToken;
// Use the first non-punctuator token in a conditional expression
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: I find this style of commenting to be pretty awkward. I think this would be best if it was moved inside the conditional branch. Don't worry about this unless you end up touching this again, especially since you didn't write it.

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 was in the area and updated the commenting style. I also find it confusing as it is.

parserOptions: parserOptions
// }, {
// should we put effort in making this work?
// who in their right mind would do this?
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think this is worth supporting.

@kentcdodds kentcdodds force-pushed the pr/jsx-indent-tabs-fix branch 2 times, most recently from 95a5f8e to fe5c7e3 Compare February 15, 2017 18:24
Copy link
Contributor Author

@kentcdodds kentcdodds left a comment

Choose a reason for hiding this comment

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

I made a few small (comments only) changes based on feedback.

@@ -165,8 +184,9 @@ module.exports = {
} while (token.type === 'JSXText' && /^\s*$/.test(token.value));
var startLine = node.loc.start.line;
var endLine = token ? token.loc.end.line : -1;
var whitespaceOnly = token ? /\n\s*$/.test(token.value) : 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 just tried out changing it to ^ and it results in an additional extraneous error in one of the test cases. Specifically:

function MyComponent(props) {
	return (
    <div
			className="foo-bar"
			id="thing"
    >
      Hello world!
    </div>
	)
}

Because token.value is actually everything between the > and the </div> (which includes a \n)

if (prevToken.type === 'JSXText' || prevToken.type === 'Punctuator' && prevToken.value === ',') {
prevToken = sourceCode.getNodeByRangeIndex(prevToken.start);
prevToken = prevToken.type === 'Literal' ? prevToken.parent : prevToken;
// Use the first non-punctuator token in a conditional expression
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 was in the area and updated the commenting style. I also find it confusing as it is.

parserOptions: parserOptions
// }, {
// should we put effort in making this work?
// who in their right mind would do this?
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great, I went ahead and removed the commented-out code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

jsx-indent with tabs fails to error/fix
5 participants