-
Notifications
You must be signed in to change notification settings - Fork 660
fix(rome_js_formatter): use parenthesis #2538
Conversation
crates/rome_js_formatter/tests/specs/js/module/class/class.js.snap
Outdated
Show resolved
Hide resolved
Parser conformance results on ubuntu-latestjs/262
jsx/babel
ts/babel
ts/microsoft
|
crates/rome_js_formatter/tests/specs/js/module/expression/unary_expression.js.snap
Outdated
Show resolved
Hide resolved
Deploying with Cloudflare Pages
|
let parent_kind = node.parent().map(|p| p.kind()); | ||
if matches!( | ||
parent_kind, | ||
Some(JsSyntaxKind::JS_FOR_STATEMENT | JsSyntaxKind::JS_RETURN_STATEMENT) | ||
) { | ||
FormatPrecedence::Low | ||
} else { | ||
FormatPrecedence::High | ||
} |
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.
This piece of logic covers:
for ((i = 0), (len = arr.length); i < len; i++) {
// ^^^^^^^^^^^^^^^^^^
console.log(arr[i])
}
let super_class = super_class?; | ||
let needs_parens = matches!( | ||
super_class.syntax().kind(), | ||
JsSyntaxKind::JS_NEW_EXPRESSION | ||
| JsSyntaxKind::JS_YIELD_EXPRESSION | ||
| JsSyntaxKind::JS_OBJECT_EXPRESSION | ||
| JsSyntaxKind::TS_NON_NULL_ASSERTION_EXPRESSION | ||
); | ||
let super_class = super_class.format_with(formatter, |super_class| { | ||
if needs_parens { | ||
format_elements![token("("), super_class, token(")")] | ||
} else { | ||
super_class | ||
} | ||
})?; |
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.
Adding parentheses to a sub-expression seems to be a common pattern. Could we introduce a format_sub_expression(expression: JsAnyExpression, formatter; &Formatter) -> FormatResult<FormatElement>
method instead that internally uses a match similar to prettier's to determine if parentheses for this expression are needed.
This would reduce the boilerplate code that now had to be added to every place where we format a sub expression.
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.
I did try this approach at first, but it didn't work for some particular cases.
- prettier's approach orbits around their architecture of formatting things, which is different from ours; by default their approach is elide parenthesis as much as they can, and then they use that monster switch to refrain the algorithm from doing so; (I didn't study it in detail, but I saw some issue where prettier was removing parenthesis where it should not, and they added some cases there... we don't have this problem here)
- their AST is different from ours, and sometimes it didn't work inside a big match; I found myself moving code from the generic function and put it inside the specific node;
- adding parenthesis requires great context, which would lead to the same issue I introduced in the sequence expressions, where I added a context but it wouldn't work for formatting range;
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.
Unrealistic situation, but any reason this doesn't match prettier?
@@ -27,10 +29,18 @@ impl FormatNode for JsUnaryExpression { | |||
); | |||
|
|||
if is_keyword_operator { | |||
let needs_parenthesis = JsAwaitExpression::can_cast(argument.syntax().kind()); |
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.
nit: we can do a match on argument directly:
let needs_parenthesis = JsAwaitExpression::can_cast(argument.syntax().kind()); | |
let needs_parenthesis = matches!(argument, JsAnyExpression::JsAwaitExpression(_)); |
_ => false, | ||
}; | ||
|
||
dbg!(&is_ambiguous_expression); |
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.
Any reason to keep this?
I think we need a more general approach to handling parentheses rather than doing it on a per-node level as discussed on discord. Do you want to explore if it would be feasible to handle parentheses inside the |
If you feel strong about this, then I am happy to delegate the implementation to someone else. As I explained in #2538 (comment), I already explored this approach and it didn't work as intended. |
What I'm looking for in an implementation is that it is possible to keep the removal of parentheses in the parenthesized expression formatting rule and the addition of parentheses in the case where they are needed in sync. |
Summary
This is part of #2449
It's a long list so it won't be fixed in one single PR. Given this file: https://github.com/prettier/prettier/blob/a9de2a128cc8eea84ddd90efdc210378a894ab6b/src/language-js/needs-parens.js
I fixed cases for:
I also did some work for the conditional expressions, although it can't be completed because at the moment we don't correctly format them.
Once we do, can add parenthesis to this case
Test Plan
I progressively added new test cases every time I covered a new case.