diff --git a/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs index 06b60def15a0..89139c86ecae 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs @@ -36,14 +36,30 @@ impl FormatNodeRule for FormatHtmlOpeningElement { r_angle_token, } = node.as_fields(); + let bracket_same_line = f.options().bracket_same_line().value(); write!(f, [l_angle_token.format(), name.format()])?; - if attributes.len() > 0 { - write!(f, [space(), attributes.format()])? - } - // When these tokens are borrowed, they are managed by the sibling `HtmlElementList` formatter. - if !self.r_angle_is_borrowed { - write!(f, [r_angle_token.format()])?; - } + + let attr_group_id = f.group_id("element-attr-group-id"); + write!( + f, + [&group(&format_with(|f| { + attributes.format().fmt(f)?; + + // Whitespace sensitivity takes precedence over bracketSameLine for correctness. + // + // The r_angle is placed inside this group because prettier always includes this token + // in the same group as the attributes, unless the token is being borrowed. + // When these tokens are borrowed, they are managed by the sibling `HtmlElementList` formatter. + if !bracket_same_line { + write!(f, [soft_line_break()])?; + } + if !self.r_angle_is_borrowed { + write!(f, [r_angle_token.format()])?; + } + Ok(()) + })) + .with_group_id(Some(attr_group_id))] + )?; Ok(()) } diff --git a/crates/biome_html_formatter/src/html/auxiliary/self_closing_element.rs b/crates/biome_html_formatter/src/html/auxiliary/self_closing_element.rs index 7810bd475975..fd3bcb03a580 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/self_closing_element.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/self_closing_element.rs @@ -12,24 +12,42 @@ impl FormatNodeRule for FormatHtmlSelfClosingElement { slash_token, r_angle_token, } = node.as_fields(); + let bracket_same_line = f.options().bracket_same_line().value(); + write!(f, [l_angle_token.format(), name.format(), space()])?; + + let attr_group_id = f.group_id("element-attr-group-id"); write!( f, - [ - l_angle_token.format(), - name.format(), - space(), - attributes.format(), - space(), - ] - )?; + [&group(&format_with(|f| { + attributes.format().fmt(f)?; + + // Whitespace sensitivity takes precedence over bracketSameLine for correctness. + // + // The r_angle is placed inside this group because prettier always includes this token + // in the same group as the attributes, unless the token is being borrowed. + // When these tokens are borrowed, they are managed by the sibling `HtmlElementList` formatter. + if bracket_same_line { + write!(f, [hard_space()])?; + } else { + write!(f, [soft_line_break_or_space()])?; + } - if slash_token.is_some() { - write!(f, [slash_token.format()])?; - } else { - write!(f, [text("/")])?; - } + // TODO: These tokens (the `/>`) are not yet borrowed by sibling elements for whitespace sensitivity. + // To resolve this, these tokens either need to be passed to or deferred to sibling text elements when + // whitespace sensitivity would require it. + if slash_token.is_some() { + write!(f, [slash_token.format()])?; + } else { + write!(f, [text("/")])?; + } + + write!(f, [r_angle_token.format()])?; + Ok(()) + })) + .with_group_id(Some(attr_group_id))] + )?; - write!(f, [r_angle_token.format()]) + Ok(()) } } diff --git a/crates/biome_html_formatter/src/html/lists/attribute_list.rs b/crates/biome_html_formatter/src/html/lists/attribute_list.rs index 9b1bf5381c58..49b9cdbf04dc 100644 --- a/crates/biome_html_formatter/src/html/lists/attribute_list.rs +++ b/crates/biome_html_formatter/src/html/lists/attribute_list.rs @@ -6,22 +6,32 @@ pub(crate) struct FormatHtmlAttributeList; impl FormatRule for FormatHtmlAttributeList { type Context = HtmlFormatContext; fn fmt(&self, node: &HtmlAttributeList, f: &mut HtmlFormatter) -> FormatResult<()> { - let attribute_len = node.iter().len(); - let line_break = if f.options().attribute_position() == AttributePosition::Multiline - && attribute_len > 1 + let attribute_count = node.len(); + let attribute_seperator = if f.options().attribute_position() + == AttributePosition::Multiline + && attribute_count > 1 { hard_line_break() } else { soft_line_break_or_space() }; - write!( - f, - [&group(&soft_block_indent(&format_with(|f| { - f.join_with(&line_break) - .entries(node.iter().formatted()) - .finish() - })))] - ) + if attribute_count > 0 { + write!( + f, + [ + space(), + &soft_line_indent_or_space(&format_with(|f| { + f.join_with(&attribute_seperator) + .entries(node.iter().formatted()) + .finish()?; + + Ok(()) + })) + ] + )?; + } + + Ok(()) } } diff --git a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html new file mode 100644 index 000000000000..9e69b68b13ce --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html @@ -0,0 +1,9 @@ +
+ hello world +
diff --git a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap new file mode 100644 index 000000000000..8d4c06008440 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/element.html.snap @@ -0,0 +1,68 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: elements/bracket-same-line/element.html +--- +# Input + +```html +
+ hello world +
+ +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: strict +Indent script and style: false +----- + +```html +
hello world
+``` + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: true +Whitespace sensitivity: strict +Indent script and style: false +----- + +```html +
hello world
+``` diff --git a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/options.json b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/options.json new file mode 100644 index 000000000000..baea0d470ac0 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/options.json @@ -0,0 +1,6 @@ +{ + "$schema": "../../../../../../../../packages/@biomejs/biome/configuration_schema.json", + "formatter": { + "bracketSameLine": true + } +} diff --git a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/self-closing.html b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/self-closing.html new file mode 100644 index 000000000000..50cf96f1ced3 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/self-closing.html @@ -0,0 +1,7 @@ + diff --git a/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/self-closing.html.snap b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/self-closing.html.snap new file mode 100644 index 000000000000..ca85d98363d7 --- /dev/null +++ b/crates/biome_html_formatter/tests/specs/html/elements/bracket-same-line/self-closing.html.snap @@ -0,0 +1,66 @@ +--- +source: crates/biome_formatter_test/src/snapshot_builder.rs +info: elements/bracket-same-line/self-closing.html +--- +# Input + +```html + + +``` + + +============================= + +# Outputs + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: false +Whitespace sensitivity: strict +Indent script and style: false +----- + +```html + +``` + +## Output 1 + +----- +Indent style: Tab +Indent width: 2 +Line ending: LF +Line width: 80 +Attribute Position: Auto +Bracket same line: true +Whitespace sensitivity: strict +Indent script and style: false +----- + +```html + +```