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

Update: Added auto-fix to multiline-ternary #12982

Closed
wants to merge 1 commit into from

Conversation

Sawtaytoes
Copy link
Contributor

@Sawtaytoes Sawtaytoes commented Feb 28, 2020

Prerequisites checklist

  • I have read the contributing guidelines.
  • The team has reached consensus on the changes proposed in this pull request. If not, I understand that the evaluation process will begin with this pull request and won't be merged until the team has reached consensus.

What is the purpose of this pull request? (put an "X" next to an item)

[ ] Documentation update
[ ] Bug fix (template)
[ ] New rule (template)
[ ] Changes an existing rule (template)
[X] Add autofixing to a rule
[ ] Add a CLI option
[ ] Add something to the core
[ ] Other, please explain:

What changes did you make? (Give an overview)

Added functionality to automatically fix multiline-ternary issues.

Is there anything you'd like reviewers to focus on?

While it may seem like doing '\n? ' is opinionated, I did have changed to make this take a new option and either put the ? on the previous line or next line. This is actually unnecessary because operator-linebreak handles it for you.

This will most-likely have to be reflected in the docs as well similar to how indent is required for every rule's ability to properly fix tabbing.

@eslint-deprecated eslint-deprecated bot added the triage An ESLint team member will look at this issue soon label Feb 28, 2020
@kaicataldo kaicataldo added enhancement This change enhances an existing feature of ESLint evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion rule Relates to ESLint's core rules and removed triage An ESLint team member will look at this issue soon labels Feb 28, 2020
@kaicataldo
Copy link
Member

Thanks for contributing. Just so you're aware, since there isn't an accepted issue for this, we'll be starting the consensus-seeking process outlined here. This can take some time, so we ask for your patience.

It's fine to wait to implement them until the team has decided whether or not we will accept this, but we also require corresponding tests for any changes in the codebase.

@Sawtaytoes
Copy link
Contributor Author

@kaicataldo Of course! I'll make sure to add tests as well :).

@mdjermanovic
Copy link
Member

I support this enhancement 👍

Wondering was there a specific reason why this rule isn't already fixable, almost all other whitespace rules are.

@Sawtaytoes
Copy link
Contributor Author

Sawtaytoes commented Mar 2, 2020

@kaicataldo
I wanted to clarify the part about there not being an existing issue.

The reason I made this PR is actually because I had an issue with ternary auto-formatting. Without auto-fixing multiline-ternary, operator-linebreak does not work properly. I noticed ? being on the end of the line where I required it be in the front.

So even though operator-linebreak auto-fixes, it only does so with the "before" configuration when multiline-ternary is providing this line-break functionality.

'operator-linebreak': [
  'warn',
  'before',
],

@mdjermanovic
Copy link
Member

The reason I made this PR is actually because I had an issue with ternary auto-formatting. Without auto-fixing multiline-ternary, operator-linebreak does not work properly. I noticed ? being on the end of the line where I required it be in the front.

Can you please provide an example where this happens?

@Sawtaytoes
Copy link
Contributor Author

Sawtaytoes commented Mar 3, 2020

@mdjermanovic

Came across this same code with different variable names in a project at work. This isn't the only instance, but it's a recurring example.

items = isSomething ? [
  ...stuff,
  ...moreStuff,
] : collection.items

Nothing happens even though the ? and : should drop down a line. If I put in the multiline-ternary adjustment, then it looks like this:

items = isSomething
  ? [
    ...stuff,
    ...moreStuff,
  ]
  : collection.items

^^ It's indenting the ternary like that because of indent.

The only rule change between these is multiline-ternary (default of always).

operator-linebreak, for whatever reason, doesn't error in this situation. Once you start messing with linebreaks for ? and :, it works great!

@Sawtaytoes
Copy link
Contributor Author

Sawtaytoes commented Mar 3, 2020

It's now passing all unit tests with output props.

The output results are a little wonky, but they work correctly if you use it together with indent and operator-linebreak. If operator-linebreak wasn't required, it'd require duplicating its functionality in multiline-ternary since you'd need to know which side to put the ? and : when line-breaking.

I updated the docs to reflect this factor.

I noticed unfixed warnings from this ESLint rule and wanted to auto-fix them. While it may seem like doing `'\n? '` is opinionated, I did have changed to make this take a new option and either put the `?` on the previous line or next line. This is actually unnecessary because `operator-linebreak` handles it for you.
@yeonjuan
Copy link
Member

👍 I agreed with this enhancement.

@kaicataldo
Copy link
Member

It looks like we just need a champion to accept this.

@aladdin-add aladdin-add self-assigned this Apr 4, 2020
@aladdin-add
Copy link
Member

it is accepted now! 🎉

@aladdin-add aladdin-add added accepted There is consensus among the team that this change meets the criteria for inclusion and removed evaluating The team will evaluate this issue to decide whether it meets the criteria for inclusion labels Apr 4, 2020
@kaicataldo kaicataldo changed the title Added auto-fix to multiline-ternary Update: Added auto-fix to multiline-ternary Apr 4, 2020
Comment on lines +139 to +141
### --fix

If this rule is invoked with the command-line `--fix` option, it's recommended to define both `indent` and `operator-linebreak` if you want to have sensible results when using the `always` and `always-multiline` options.
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if this documentation update is necessary.

The same applies to a lot of fixable rules, but we usually don't have these sections since it's reasonable to expect that other formatting rules are also enabled and configured. operator-linebreak is already listed in "Related Rules".

This detailed explanation with examples is certainly useful, but it's also an additional thing to maintain and keep in sync with other rules.

Thoughts from the team?

Comment on lines +62 to +68
const testToken = astUtils.isParenthesised(sourceCode, node.test)
? sourceCode.getTokenAfter(node.test)
: node.test;

const consequentToken = astUtils.isParenthesised(sourceCode, node.consequent)
? sourceCode.getTokenBefore(node.consequent)
: node.consequent;
Copy link
Member

Choose a reason for hiding this comment

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

This would produce invalid autofix for a code with multiple parentheses around any of the operands, for example:

/* eslint multiline-ternary: ["error", "never"] */

((a)) ?
b : c;

will be autofixed to:

/* eslint multiline-ternary: ["error", "never"] */

((a) ? b : c;

Maybe we could slightly change the logic, to something like how operator-linebreak finds these tokens.

(the same applies to if (!areConsequentAndAlternateOnSameLine))

Comment on lines +71 to +76
fixer.replaceTextRange(
[
testToken.range[1],
consequentToken.range[0]
],
" ? "
Copy link
Member

Choose a reason for hiding this comment

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

This can remove comments, for example:

/* eslint multiline-ternary: ["error", "never"] */

a ? // comment
b : c;

will be autofixed to:

/* eslint multiline-ternary: ["error", "never"] */

a ? b : c;

In my opinion, for this particular rule it would be fine to not autofix code (return null) if there are any comments within the range between the two tokens.

(the same applies to if (!areConsequentAndAlternateOnSameLine)).

Comment on lines +126 to +133
return (
fixer.replaceTextRange(
[
testToken.range[1],
consequentToken.range[0]
],
"\n? "
)
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we could just insert \n before ? instead of replacing the range? I believe all other newline rules work that way.

(the same applies to if (areConsequentAndAlternateOnSameLine))

@mdjermanovic
Copy link
Member

#13195 might be relevant to this.

@kaicataldo
Copy link
Member

@Sawtaytoes Anything else we can do to help you land this?

@Sawtaytoes
Copy link
Contributor Author

Hey, I can take a look again this weekend and see what needs done.

@Sawtaytoes
Copy link
Contributor Author

Sawtaytoes commented May 21, 2020

Sadly, I didn't have time last weekend.

@mdjermanovic
Copy link
Member

#13367 changes how this rule works (a bit), and adds some code that might be helpful for this, so maybe we could merge #13367 first and then continue with this PR on top of those changes?

@nzakas
Copy link
Member

nzakas commented Jun 11, 2020

It looks like #13367 has been merged.

@Sawtaytoes can you rebase onto master?

@mdjermanovic
Copy link
Member

@Sawtaytoes it would be great to have this feature, is there anything we can do to help?

@Sawtaytoes
Copy link
Contributor Author

@mdjermanovic If you wanna pair code, that'd be great. At this point, it's been so long I'd need to refresh myself on what I was even doing, how I tested, etc.

I had a second kid since I made the original PR, so I haven't had as much time to spend in off-work coding. But if I had someone to pair with, I'm sure we could figure it out, write up any remaining requirements for someone else to pick up (or me), and get this merged!

@vegerot
Copy link
Contributor

vegerot commented Dec 21, 2020

What else needs to be done to get this merged in?

@nzakas
Copy link
Member

nzakas commented Dec 23, 2020

@vegerot it looks like we would need someone to take over this PR to get it moving again.

@mdjermanovic
Copy link
Member

This should be easier with the current version of this rule. All relevant tokens are already there, and there's no need to check for parentheses.

const questionToken = sourceCode.getTokenAfter(node.test, astUtils.isNotClosingParenToken);
const colonToken = sourceCode.getTokenAfter(node.consequent, astUtils.isNotClosingParenToken);
const firstTokenOfTest = sourceCode.getFirstToken(node);
const lastTokenOfTest = sourceCode.getTokenBefore(questionToken);
const firstTokenOfConsequent = sourceCode.getTokenAfter(questionToken);
const lastTokenOfConsequent = sourceCode.getTokenBefore(colonToken);
const firstTokenOfAlternate = sourceCode.getTokenAfter(colonToken);

It's fine to just return null if there are any comments within the range that should be fixed.

Documentation update doesn't seem necessary.

@aladdin-add
Copy link
Member

ha, thanks @mdjermanovic pointing! I can take over and update it later.

@aladdin-add aladdin-add closed this Jan 1, 2021
@eslint-github-bot eslint-github-bot bot locked and limited conversation to collaborators Jul 1, 2021
@eslint-github-bot eslint-github-bot bot added the archived due to age This issue has been archived; please open a new issue for any further discussion label Jul 1, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
accepted There is consensus among the team that this change meets the criteria for inclusion archived due to age This issue has been archived; please open a new issue for any further discussion enhancement This change enhances an existing feature of ESLint rule Relates to ESLint's core rules
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants