feat(rome_js_formatter): Refine variable declaration formatting
MODIFY
crates/rome_js_formatter/src/js/declarations/variable_declaration.rs
crates/rome_js_formatter/src/js/declarations/variable_declaration.rs
@@ -11,6 +11,6 @@ impl FormatNodeRule<JsVariableDeclaration> for FormatJsVariableDeclaration {
fn fmt_fields(&self, node: &JsVariableDeclaration, f: &mut JsFormatter) -> FormatResult<()> {
let JsVariableDeclarationFields { kind, declarators } = node.as_fields();
- write![f, [kind.format(), space(), declarators.format(),]]
+ write!(f, [kind.format(), space(), declarators.format()])
}
}

MODIFY
crates/rome_js_formatter/src/js/lists/variable_declarator_list.rs
crates/rome_js_formatter/src/js/lists/variable_declarator_list.rs
use crate::prelude::*;
-use rome_formatter::{format_args, write};
+use rome_formatter::write;
-use crate::AsFormat;
use rome_js_syntax::{JsSyntaxKind, JsVariableDeclaratorList};
use rome_rowan::AstSeparatedList;
@@ -12,50 +11,60 @@ impl FormatRule<JsVariableDeclaratorList> for FormatJsVariableDeclaratorList {
type Context = JsFormatContext;
fn fmt(&self, node: &JsVariableDeclaratorList, f: &mut JsFormatter) -> FormatResult<()> {
- let last_index = node.len().saturating_sub(1);
+ let format_inner = format_with(|f| {
+ let length = node.len();
- let mut declarators = node.elements().enumerate().map(|(index, element)| {
- format_with(move |f| {
- write!(f, [&element.node().format()])?;
+ let is_parent_for_loop = node.syntax().grand_parent().map_or(false, |grand_parent| {
+ matches!(
+ grand_parent.kind(),
+ JsSyntaxKind::JS_FOR_STATEMENT
+ | JsSyntaxKind::JS_FOR_OF_STATEMENT
+ | JsSyntaxKind::JS_FOR_IN_STATEMENT
+ )
+ });
- match element.trailing_separator()? {
- None => {
- if index != last_index {
- format_inserted(JsSyntaxKind::COMMA).fmt(f)?;
- }
- }
- Some(separator) => {
- if index != last_index {
- write!(f, [separator.format()])?;
- }
- }
- };
+ let has_any_initializer = node.iter().any(|declarator| {
+ declarator.map_or(false, |declarator| declarator.initializer().is_some())
+ });
- Ok(())
- })
+ let format_separator = format_with(|f| {
+ if !is_parent_for_loop && has_any_initializer {
+ write!(f, [hard_line_break()])
+ } else {
+ write!(f, [soft_line_break_or_space()])
+ }
});
- let leading_element = declarators.next().ok_or(FormatError::SyntaxError)?;
+ let mut declarators = node.iter().zip(
+ node.format_separated(JsSyntaxKind::COMMA)
+ .with_trailing_separator(TrailingSeparator::Disallowed),
+ );
- let other_declarators = format_once(|f| {
- if node.len() == 1 {
- // No more declarators, avoid single line break
- return Ok(());
+ let (first_declarator, format_first_declarator) = match declarators.next() {
+ Some((syntax, format_first_declarator)) => (syntax?, format_first_declarator),
+ None => return Err(FormatError::SyntaxError),
+ };
+
+ if length == 1 && !first_declarator.syntax().has_leading_comments() {
+ return write!(f, [format_first_declarator]);
}
- write!(f, [soft_line_break_or_space()])?;
+ write!(
+ f,
+ [indent(&format_once(|f| {
+ write!(f, [format_first_declarator])?;
+
+ if length > 1 {
+ write!(f, [format_separator])?;
+ }
- f.join_with(&soft_line_break_or_space())
- .entries(declarators)
+ f.join_with(&format_separator)
+ .entries(declarators.map(|(_, format)| format))
.finish()
+ }))]
+ )
});
- write!(
- f,
- [group(&format_args!(
- leading_element,
- indent(&other_declarators)
- ))]
- )
+ write!(f, [group(&format_inner)])
}
}

MODIFY
crates/rome_js_formatter/src/js/statements/expression_statement.rs
crates/rome_js_formatter/src/js/statements/expression_statement.rs
use crate::prelude::*;
-use rome_formatter::{format_args, write};
+use rome_formatter::write;
use crate::utils::FormatWithSemicolon;
@@ -19,7 +19,7 @@ impl FormatNodeRule<JsExpressionStatement> for FormatJsExpressionStatement {
write!(
f,
[FormatWithSemicolon::new(
- &format_args!(expression.format()),
+ &expression.format(),
semicolon_token.as_ref()
)]
)

MODIFY
crates/rome_js_formatter/tests/specs/prettier/js/comments/variable_declarator.js.snap
crates/rome_js_formatter/tests/specs/prettier/js/comments/variable_declarator.js.snap
@@ -79,7 +79,7 @@ const foo3 = 123
```diff
--- Prettier
+++ Rome
-@@ -38,35 +38,29 @@
+@@ -38,35 +38,31 @@
"val",
];
@@ -119,8 +119,9 @@ const foo3 = 123
-let // Comment
- foo2 = "val",
-- bar = "val";
-+let foo2 = "val", bar = "val"; // Comment
++let foo2 = "val",
++ // Comment
+ bar = "val";
-const foo3 = 123;
+const foo3 = 123
@@ -192,7 +193,9 @@ val
let foo1 = "val"; // Comment
-let foo2 = "val", bar = "val"; // Comment
+let foo2 = "val",
+ // Comment
+ bar = "val";
const foo3 = 123
// Nothing to see here.

MODIFY
crates/rome_rowan/src/ast/mod.rs
crates/rome_rowan/src/ast/mod.rs
@@ -364,6 +364,16 @@ pub trait AstSeparatedList {
}
}
+ /// Returns the first node
+ fn first(&self) -> Option<SyntaxResult<Self::Node>> {
+ self.iter().next()
+ }
+
+ /// Returns the last node
+ fn last(&self) -> Option<SyntaxResult<Self::Node>> {
+ self.iter().next_back()
+ }
+
#[inline]
fn is_empty(&self) -> bool {
self.len() == 0

MODIFY
crates/rome_rowan/src/syntax/node.rs
crates/rome_rowan/src/syntax/node.rs
@@ -248,6 +248,11 @@ impl<L: Language> SyntaxNode<L> {
self.raw.parent().map(Self::from)
}
+ /// Returns the grand parent.
+ pub fn grand_parent(&self) -> Option<SyntaxNode<L>> {
+ self.parent().and_then(|parent| parent.parent())
+ }
+
/// Returns the index of this node inside of its parent
#[inline]
pub(crate) fn index(&self) -> usize {

feat(rome_js_formatter): Refine variable declaration formatting (#2983)