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
+
+```