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
New: Add offsetTernaryExpressions option for indent rule #12556
Conversation
b96c3b4
to
8621041
Compare
I've fixed some of the examples I've described in description of this issue |
When I look at jsx rules in condition
? <x>
y
</x>
: null with
Trying to correct it by doing condition
? <x>
y
</x>
: null fails with
The only way (that I know of) to satisfy condition
? (
<x>
y
</x>
)
: null (Ofcourse to solve the issue, you could also put the jsx element on a single line, or store it in an intermediate variable. But that would apply tot he original problem as well, so I left those out.) Now, it seems to me that the case presented in this PR is quite analog to the jsx one. And it seems to me that in that case, the expectation would be to format it as: search.premium
? (
commission => {
return suggestions.hits.map(hit => something(hit))
}
)
: (
something
? (
commission => {
return suggestions.hits.map(hit => something(hit))
}
)
: null
) Ofcourse, that is unless the jsx behavior was unintend, or unless consistency between these two variants of ternary is not important. |
@Janpot It seems that this PR would fix your issue as well, because: condition
? <x>
y
</x>
: null doesn't return any errors when As for your last example this PR would format it as: search.premium
? (
commission => {
return suggestions.hits.map(hit => something(hit))
}
)
: (
something
? (
commission => {
return suggestions.hits.map(hit => something(hit))
}
)
: null
) but the parenthesis are not necessary, and without them it formats it more nicely: search.premium
? commission => {
return suggestions.hits.map(hit => something(hit))
}
: something
? commission => {
return suggestions.hits.map(hit => something(hit))
}
: null |
OK, seems like "unless the jsx behavior was unintend" applies here. LGTM then 👍 |
@sheerun How would this work with indentation !== 2 spaces? What in case of > 2 spaces? What if tabs are used? |
@Janpot This style is not really compatible with 4-spaces indentation, because it would introduce 6-spaces indentation or 10-spaces indentation in many places... Here's example: search.premium
? commission => { // 4 spaces
return suggestions.hits.map(hit => something(hit)) // 10 spaces?
} // 6 spaces?
: something alternative: search.premium
? commission => { // 4 spaces + 2 spaces?
return suggestions.hits.map(hit => something(hit)) // 12 spaces
} // 8 spaces
: something // what? |
Given the popularity of 2-space indentation, what if we made it so that this option only applies if you also have 2-space indentation? Either that, or just trust that people without 2-space indentation won't use this option unless they're ok with the output |
@josephfrazier 2-space, 4-space, and tabs are all popular enough that they all should be considered. |
@ljharb I'm indifferent what happens if 4-spaces and tabs are used. Can you suggest what the formatting should be that eslint is going to accept? Best by example. |
@sheerun /*eslint indent: ["error", 4, { "offsetTernaryExpressions": true }]*/
condition
? () => {
return true
}
: condition2
? () => {
return true
}
: () => {
return false
} /*eslint indent: ["error", 'tab', { "offsetTernaryExpressions": true }]*/
condition
? () => {
return true
}
: condition2
? () => {
return true
}
: () => {
return false
} (the tabs example will ofc look horrible at the default 8-space width, but a) that's the whole point of tabs - their width is configurable, so you're not oppressed into someone else's preferred width, and b) everyone uses a 2-space or 4-space width for their tabs anyways. |
@ljharb I don't get you examples in some places. They deviate very much from "basic" eslint indentation rules so fixing them your way would require a lot of code and hacks. In particular nested /*eslint indent: ["error", 4, { "offsetTernaryExpressions": true }]*/
condition
? () => {
return true // 12 spaces
} // 8 spaces
: condition2
? () => { // 7 spaces?
return true // again 12 spaces even though nested ternary is indented?
} // again 8 spaces even though ternary is nested?
: () => { // 7 spaces?
return false // again 12 spaces even though nested ternary is indented?
} // again 8 spaces even though ternary is nested? condition
? () => {
return true
}
: condition2
? () => { // why ? is at the same level?
return true
}
: () => {
return false
} My suggestion is to use following for 4-spaces indent that is consistent: condition
? () => { // 4 spaces
return true // 12 spaces
} // 8 spaces
: condition2 // 4 spaces
? () => { // 8 spaces
return true // 16 spaces
} // 12 spaces
: () => { // 8 spaces
return false // 16 spaces
} // 12 spaces As you can see this makes number of spaces everywhere a multiply of 4 and makes indentation with tabs + nested indentation for |
@sheerun the 7 spaces is a mistake :-) all i did is take your 2-space example, and attempt to double the number of spaces; and then take it again and convert each 2-space to a Indentation rules should deal in indentation "levels" - i should be able to set it to 57 spaces, and 3 indentation levels thus produces 171 spaces; or 1 space, and thus 3 spaces. The number or "tab" shouldn't impact how the rules work, just which character(s) represent "one indentation level". |
@ljharb According to what you're saying my last example should be correct one, correct? And here's additional correct example with tabs: condition
? () => { // 1 tab
return true // 3 tabs
} // 2 tabs
: condition2 // 1 tab
? () => { // 2 tabs
return true // 4 tabs
} // 3 tabs
: () => { // 2 tabs
return false // 4 tabs
} // 3 tabs |
Looks right to me! |
8621041
to
1851ac8
Compare
@ljharb Because there are no changes needed in code to format according to these examples, I've just added two more tests that verify 4-spaces and tabs indentation. I hope this makes this PR complete. |
I support this change, but can you speak to why you think it’s better to add |
I don't really care what this is called. Please suggest something you're willing to merge if |
I will champion this. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Generally looks good to me, thanks!
Two requested changes:
- Could you please add one or more
invalid
unit tests that use the new option, to show how autofix should fix the test cases into the correct format? - Could you please change the commit summary to begin with "Update"? Although this is a new option, it's an enhancement to an existing rule, and we traditionally reserve the "New" tag for new rules, formatters, or completely new core features.
Thanks for contributing!
@platinumazure @ilyavolodin Any thoughts on the naming of this option, as mentioned here? |
@kaicataldo I slightly prefer the current name if the implementation is otherwise unchanged. Some thoughts:
|
That makes sense to me. Let’s stick with this API then. |
1851ac8
to
33190e9
Compare
@platinumazure I've applied requested changes I've added just one invalid example but it should cover all code paths, is it ok? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this. This is looking great! Just a few small comments from me.
bb4eab5
to
b3294cb
Compare
@kaicataldo done |
Could we add just one valid test with the new option set to |
b3294cb
to
5f47f9f
Compare
@kaicataldo done |
This has now been accepted. |
@kaicataldo hello :) what's the release schedule for this looking like? |
@concertcoder It will be published in the release after it's approved and merged (we currently do regular monthly releases). Hopefully in the next release! |
@platinumazure Have your concerns been addressed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me, thanks for making the changes I requested!
I would like to apologize for letting this slip through the cracks. I've been very busy with my work and haven't been able to stay on top of open source. That's not an excuse, though, and I am sorry for the poor experience.
Can you release 6.9.0 out of the schedule? |
Thanks for contributing! @sheerun Unfortunately, I don't think we'll be able to do that. This will be released in the next release! |
What is the purpose of this pull request?
Adds
offsetTernaryExpressions
boolean indent rule that solves following issues:Please describe what the rule should do:
Functions are normally formatted with indentation which looks nice:
but when ternary expression is used suddenly
return
is at the same level as argument:This is just one example of many of issues described in referenced github issues, that is: the values of ternary expression are indented at the same level as
?
and:
instead of having its own indent level.Here's an example with multi-level
?:
. That's how it is currently:And that's what many developers are expecting:
Ternary expressions are very often used in JSX so this this issue is especially annoying. For example this is how this rule formats code currently:
And that's what many developers are expecting:
I get that it's not possible to make this rule a default, so I'm introducing
offsetTernaryExpressions
rules that allows to fix the number of issues above.Actually this rule also makes it possible to indent ternary expressions similarly to how prettier does it. Here is multiline ternary expression as formatted by prettier:
Please note that prettier is putting
?:
at the same level as nested?:
, but this rule is very separate from properly indentingcommission => { ... }
expressions.What category of rule is this? (place an "X" next to just one item)
[X] Enforces code style
[ ] Warns about a potential error
[ ] Suggests an alternate way of doing something
[ ] Other (please specify:)
Provide 2-3 code examples that this rule will warn about:
Why should this rule be included in ESLint (instead of a plugin)?
Enforcing indentation is deeply integrated into eslint. It's not efficient to do it in plugin.
What changes did you make? (Give an overview)
offsetTernaryExpressions
rules that enforces the style described above