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..d4bba71dd4e6 100644 --- a/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs +++ b/crates/biome_html_formatter/src/html/auxiliary/opening_element.rs @@ -1,5 +1,5 @@ use crate::prelude::*; -use biome_formatter::{write, FormatRuleWithOptions}; +use biome_formatter::{write, AttributePosition, FormatRuleWithOptions}; use biome_html_syntax::{HtmlOpeningElement, HtmlOpeningElementFields}; #[derive(Debug, Clone, Default)] pub(crate) struct FormatHtmlOpeningElement { @@ -36,14 +36,48 @@ 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 line_break = if f.options().attribute_position() == AttributePosition::Multiline { + hard_line_break() + } else { + soft_line_break_or_space() + }; + + let attr_group_id = f.group_id("element-attr-group-id"); + write!( + f, + [&group(&format_with(|f| { + if attributes.len() > 0 { + write!( + f, + [ + space(), + &soft_line_indent_or_space(&format_with(|f| { + f.join_with(&line_break) + .entries(attributes.iter().formatted()) + .finish()?; + + Ok(()) + })) + ] + )?; + } + // 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..416ff091d72c 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 @@ -1,5 +1,5 @@ use crate::prelude::*; -use biome_formatter::write; +use biome_formatter::{write, AttributePosition}; use biome_html_syntax::{HtmlSelfClosingElement, HtmlSelfClosingElementFields}; #[derive(Debug, Clone, Default)] pub(crate) struct FormatHtmlSelfClosingElement; @@ -12,24 +12,58 @@ 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 line_break = if f.options().attribute_position() == AttributePosition::Multiline { + hard_line_break() + } else { + soft_line_break_or_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| { + if attributes.len() > 0 { + write!( + f, + [ + space(), + &soft_line_indent_or_space(&format_with(|f| { + f.join_with(&line_break) + .entries(attributes.iter().formatted()) + .finish()?; - if slash_token.is_some() { - write!(f, [slash_token.format()])?; - } else { - write!(f, [text("/")])?; - } + Ok(()) + })) + ] + )?; + } + // 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()])?; + } + + // TODO: These tokens (the `/>`) are not yet borrowed by sibling elements for whitespace sensitivity. + 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 595cc92eda49..e14b3bfacb25 100644 --- a/crates/biome_html_formatter/src/html/lists/attribute_list.rs +++ b/crates/biome_html_formatter/src/html/lists/attribute_list.rs @@ -1,24 +1,14 @@ use crate::prelude::*; -use biome_formatter::{write, AttributePosition}; use biome_html_syntax::HtmlAttributeList; #[derive(Debug, Clone, Default)] pub(crate) struct FormatHtmlAttributeList; impl FormatRule for FormatHtmlAttributeList { type Context = HtmlFormatContext; fn fmt(&self, node: &HtmlAttributeList, f: &mut HtmlFormatter) -> FormatResult<()> { - let line_break = if f.options().attribute_position() == AttributePosition::Multiline { - 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() - })))] - ) + // The formatting of this node is handled by `HtmlOpeningElement` and `HtmlSelfClosingElement` instead. + // + // This is because whether or not the element children breaks is partially dependent whether or not the + // attributes break, and to be able to handle the `bracketSameLine` option correctly. + format_verbatim_node(node.syntax()).fmt(f) } } 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 + +```