refactor(rome_js_formatter): format static member expressions using loop
MODIFY
crates/rome_js_formatter/src/js/expressions/static_member_expression.rs
crates/rome_js_formatter/src/js/expressions/static_member_expression.rs
-use rome_js_syntax::JsSyntaxKind;
-use rome_rowan::AstNode;
-
use crate::prelude::*;
-
use crate::FormatNodeFields;
-use rome_js_syntax::JsStaticMemberExpression;
-use rome_js_syntax::JsStaticMemberExpressionFields;
+use rome_formatter::ConcatBuilder;
+use rome_js_syntax::{
+ JsAnyExpression, JsAnyLiteralExpression, JsAnyName, JsStaticMemberExpression,
+ JsStaticMemberExpressionFields, JsSyntaxNode, JsSyntaxToken,
+};
+use rome_rowan::AstNode;
+use std::ops::Deref;
impl FormatNodeFields<JsStaticMemberExpression> for FormatNodeRule<JsStaticMemberExpression> {
fn format_fields(
node: &JsStaticMemberExpression,
formatter: &JsFormatter,
) -> FormatResult<FormatElement> {
+ let mut current = node.clone();
+
+ while let Some(static_member_expression) =
+ JsStaticMemberExpression::cast(current.object()?.into_syntax())
+ {
+ current = static_member_expression;
+ }
+
let JsStaticMemberExpressionFields {
object,
operator_token,
member,
- } = node.as_fields();
+ } = current.as_fields();
+
+ let mut formatted = ConcatBuilder::default();
+
+ let operator_token = operator_token?;
+ let formatted_object =
+ FormatMemberStaticExpression::from_expression(object?, &operator_token)
+ .format(formatter)?;
+
+ formatted.entry(formatted![
+ formatter,
+ [formatted_object, operator_token.format()]
+ ]?);
+
+ let mut previous_member = member?;
+
+ while let Some(parent_static_member_expression) = current
+ .syntax()
+ .parent()
+ .and_then(JsStaticMemberExpression::cast)
+ {
+ let JsStaticMemberExpressionFields {
+ object: _,
+ operator_token,
+ member,
+ } = parent_static_member_expression.as_fields();
+
+ let operator_token = operator_token?;
+
+ formatted.entry(group_elements(formatted![
+ formatter,
+ [
+ FormatMemberStaticExpression::from_name(previous_member, &operator_token),
+ operator_token.format(),
+ ]
+ ]?));
+
+ previous_member = member?;
+ current = parent_static_member_expression;
+ }
- let object_syntax = object.clone()?.syntax().clone();
+ formatted.entry(formatted![formatter, [previous_member.format(),]]?);
+ Ok(group_elements(formatted.finish()))
+ }
+}
+
+enum FormatMemberStaticExpression<'t> {
+ JsAnyName(JsAnyName, &'t JsSyntaxToken),
+ JsAnyExpression(JsAnyExpression, &'t JsSyntaxToken),
+}
+
+impl<'t> FormatMemberStaticExpression<'t> {
+ fn from_expression(node: JsAnyExpression, operator: &'t JsSyntaxToken) -> Self {
+ Self::JsAnyExpression(node, operator)
+ }
+ fn from_name(node: JsAnyName, operator: &'t JsSyntaxToken) -> Self {
+ Self::JsAnyName(node, operator)
+ }
+
+ fn is_number_literal_expression(&self) -> bool {
+ matches!(
+ self,
+ FormatMemberStaticExpression::JsAnyExpression(
+ JsAnyExpression::JsAnyLiteralExpression(
+ JsAnyLiteralExpression::JsNumberLiteralExpression(_)
+ ),
+ ..
+ )
+ )
+ }
+
+ fn syntax(&self) -> &JsSyntaxNode {
+ match self {
+ FormatMemberStaticExpression::JsAnyName(node, _) => node.syntax(),
+ FormatMemberStaticExpression::JsAnyExpression(node, _) => node.syntax(),
+ }
+ }
- let is_object_number_literal =
- object_syntax.kind() == JsSyntaxKind::JS_NUMBER_LITERAL_EXPRESSION;
+ fn operator(&self) -> &JsSyntaxToken {
+ match self {
+ FormatMemberStaticExpression::JsAnyName(_, operator) => operator,
+ FormatMemberStaticExpression::JsAnyExpression(_, operator) => operator,
+ }
+ }
+}
+
+impl<'t> Deref for FormatMemberStaticExpression<'t> {
+ type Target = JsSyntaxNode;
+
+ fn deref(&self) -> &Self::Target {
+ self.syntax()
+ }
+}
+
+impl<'t> Format for FormatMemberStaticExpression<'t> {
+ type Context = JsFormatContext;
+
+ fn format(&self, formatter: &JsFormatter) -> FormatResult<FormatElement> {
+ let formatted_member = formatted![formatter, [self.syntax().format()]]?;
- let has_object_trailing_trivia =
- object_syntax.last_trailing_trivia().unwrap().pieces().len() > 0;
- let has_operator_leading_trivia =
- operator_token.clone()?.leading_trivia().pieces().len() > 0;
+ let is_member_number_literal = self.is_number_literal_expression();
- let formatted_object = formatted![formatter, [object?.format()]]?;
+ let object_has_trailing_trivia = self
+ .last_trailing_trivia()
+ .map_or(false, |trivia| trivia.pieces().len() > 0);
- if is_object_number_literal && (has_object_trailing_trivia || has_operator_leading_trivia) {
- let (object_leading, object_content, object_trailing) = formatted_object.split_trivia();
+ let operator_has_leading_trivia = self.operator().leading_trivia().pieces().len() > 0;
- Ok(group_elements(formatted![
+ if is_member_number_literal && (object_has_trailing_trivia || operator_has_leading_trivia) {
+ let (object_leading, object_content, object_trailing) = formatted_member.split_trivia();
+
+ Ok(formatted![
formatter,
[
object_leading,
@@ -41,15 +144,10 @@ impl FormatNodeFields<JsStaticMemberExpression> for FormatNodeRule<JsStaticMembe
object_content,
token(")"),
object_trailing,
- operator_token.format(),
- member.format(),
]
- ]?))
+ ]?)
} else {
- Ok(group_elements(formatted![
- formatter,
- [formatted_object, operator_token.format(), member.format(),]
- ]?))
+ Ok(formatted_member)
}
}
}

MODIFY
crates/rome_js_formatter/src/lib.rs
crates/rome_js_formatter/src/lib.rs
@@ -484,24 +484,8 @@ mod test {
#[ignore]
// use this test check if your snippet prints as you wish, without using a snapshot
fn quick_test() {
- let src = r#"
- const AspectRatioBox = ({
- aspectRatio,
- children,
- ...props
-}) => (
- <div
- className={`height: 0;
- overflow: hidden;
- padding-top: ${props => 100 / props.aspectRatio}%;
- background: white;
- position: relative;`}
- >
- <div>{children}</div>
- </div>
-);
- "#;
- let syntax = SourceType::jsx();
+ let src = r#"one.two.tree"#;
+ let syntax = SourceType::tsx();
let tree = parse(src, 0, syntax);
let result = format_node(JsFormatContext::default(), &tree.syntax())
.unwrap()
@@ -513,5 +497,10 @@ mod test {
file_name: "quick_test",
format_context: JsFormatContext::default(),
});
+ assert_eq!(
+ result.as_code(),
+ r#""\a";
+"#
+ );
}
}

RENAME
crates/rome_js_formatter/tests/specs/js/module/number/number-with-space.js
crates/rome_js_formatter/tests/specs/js/module/number/number_with_space.js
RENAME
crates/rome_js_formatter/tests/specs/js/module/number/number-with-space.js.snap
crates/rome_js_formatter/tests/specs/js/module/number/number_with_space.js.snap
---
source: crates/rome_js_formatter/tests/spec_test.rs
-expression: number-with-space.js
+assertion_line: 257
+expression: number_with_space.js
---
# Input
123 .toString

refactor(rome_js_formatter): format static member expressions using loop (#2635)