diff --git a/crates/rome_formatter/src/buffer.rs b/crates/rome_formatter/src/buffer.rs index d9e2e184bc54..23007de7d4d8 100644 --- a/crates/rome_formatter/src/buffer.rs +++ b/crates/rome_formatter/src/buffer.rs @@ -199,6 +199,11 @@ impl<'a, Context> VecBuffer<'a, Context> { FormatElement::List(List::new(std::mem::take(&mut self.elements))) } } + + /// Returns a reference to the current context + pub fn context(&self) -> &Context { + &self.state.context + } } impl Deref for VecBuffer<'_, Context> { diff --git a/crates/rome_js_formatter/src/utils/assignment_like.rs b/crates/rome_js_formatter/src/utils/assignment_like.rs index 14b1897582db..918e02b6963c 100644 --- a/crates/rome_js_formatter/src/utils/assignment_like.rs +++ b/crates/rome_js_formatter/src/utils/assignment_like.rs @@ -1,7 +1,7 @@ use crate::prelude::*; use crate::utils::object::write_member_name; use crate::utils::JsAnyBinaryLikeExpression; -use rome_formatter::{format_args, write}; +use rome_formatter::{format_args, write, VecBuffer}; use rome_js_syntax::{ JsAnyAssignmentPattern, JsAnyExpression, JsAnyFunctionBody, JsAnyObjectAssignmentPatternMember, JsAnyObjectBindingPatternMember, JsAnyObjectMemberName, JsAssignmentExpression, @@ -115,7 +115,7 @@ impl Format for RightAssignmentLike { /// Assignment like are: /// - Assignment /// - Object property member -#[derive(Debug)] +#[derive(Debug, Eq, PartialEq)] pub(crate) enum AssignmentLikeLayout { /// First break right-hand side, then after operator. /// ```js @@ -235,23 +235,23 @@ impl JsAnyAssignmentLike { const MIN_OVERLAP_FOR_BREAK: u8 = 3; impl JsAnyAssignmentLike { - fn write_left(&self, f: &mut JsFormatter) -> FormatResult { + fn write_left(&self, buffer: &mut VecBuffer) -> FormatResult { match self { JsAnyAssignmentLike::JsPropertyObjectMember(property) => { - let width = write_member_name(&property.name()?, f)?; + let width = write_member_name(&property.name()?, buffer)?; let text_width_for_break = - (f.context().tab_width() + MIN_OVERLAP_FOR_BREAK) as usize; + (buffer.context().tab_width() + MIN_OVERLAP_FOR_BREAK) as usize; Ok(width < text_width_for_break) } JsAnyAssignmentLike::JsAssignmentExpression(assignment) => { let left = assignment.left()?; - write!(f, [group_elements(&left.format())])?; + write!(buffer, [&left.format()])?; Ok(false) } JsAnyAssignmentLike::JsObjectAssignmentPatternProperty(property) => { - let width = write_member_name(&property.member()?, f)?; + let width = write_member_name(&property.member()?, buffer)?; let text_width_for_break = - (f.context().tab_width() + MIN_OVERLAP_FOR_BREAK) as usize; + (buffer.context().tab_width() + MIN_OVERLAP_FOR_BREAK) as usize; Ok(width < text_width_for_break) } } @@ -502,14 +502,40 @@ pub(crate) fn has_new_line_before_comment(node: &JsSyntaxNode) -> bool { impl Format for JsAnyAssignmentLike { fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> { let format_content = format_with(|f| { + // We create a temporary buffer because the left hand side had to conditionally add + // a group based on the layout, but the layout can be computed when the left hand + // side is actually formatted. + // + // 1. we crate a temporary buffer + // 2. we write the left hand side into the buffer and retrieve the `is_left_short` info + // 3. we compute the layout + // 4. we write the left node inside the main buffer based on the layout + let mut buffer = VecBuffer::new(f.state_mut()); + let is_left_short = self.write_left(&mut buffer)?; + // Compare name only if we are in a position of computing it. // If not (for example, left is not an identifier), then let's fallback to false, // so we can continue the chain of checks - let is_left_short = self.write_left(f)?; - self.write_operator(f)?; - let layout = self.layout(is_left_short)?; + let formatted_element = buffer.into_element(); + + if layout == AssignmentLikeLayout::BreakLeftHandSide { + write!( + f, + [&format_once(|f| { f.write_element(formatted_element) })] + )?; + } else { + write!( + f, + [group_elements(&format_once(|f| { + f.write_element(formatted_element) + }))] + )?; + } + + self.write_operator(f)?; + let right = &format_with(|f| self.write_right(f)).memoized(); let inner_content = format_with(|f| match &layout { diff --git a/crates/rome_js_formatter/src/utils/object.rs b/crates/rome_js_formatter/src/utils/object.rs index 425d135419d0..738a21bc30e1 100644 --- a/crates/rome_js_formatter/src/utils/object.rs +++ b/crates/rome_js_formatter/src/utils/object.rs @@ -2,7 +2,7 @@ use crate::prelude::*; use crate::utils::FormatLiteralStringToken; use crate::utils::StringLiteralParentKind; -use rome_formatter::write; +use rome_formatter::{write, VecBuffer}; use rome_js_syntax::JsAnyObjectMemberName; use rome_js_syntax::JsSyntaxKind::JS_STRING_LITERAL; use rome_rowan::AstNode; @@ -10,7 +10,7 @@ use unicode_width::UnicodeWidthStr; pub(crate) fn write_member_name( name: &JsAnyObjectMemberName, - f: &mut JsFormatter, + buffer: &mut VecBuffer, ) -> FormatResult { match name { name @ JsAnyObjectMemberName::JsLiteralMemberName(literal) => { @@ -18,19 +18,19 @@ pub(crate) fn write_member_name( if value.kind() == JS_STRING_LITERAL { let format = FormatLiteralStringToken::new(&value, StringLiteralParentKind::Member); - let cleaned = format.clean_text(f.context()); + let cleaned = format.clean_text(buffer.context()); - cleaned.fmt(f)?; + write!(buffer, [cleaned])?; Ok(cleaned.width()) } else { - name.format().fmt(f)?; + write!(buffer, [name.format()])?; Ok(value.text_trimmed().width()) } } name => { - write!(f, [group_elements(&name.format())])?; + write!(buffer, [&name.format()])?; Ok(name.text().width()) } }