Repository: biomejs/biome (language: rust)

Issue:
📝 HTML Formatter Bug: Extra spaces inserted in quoted attributes
### Environment information

```bash
CLI:
  Version:                      0.0.0
  Color support:                true

Platform:
  CPU Architecture:             x86_64
  OS:                           linux

Environment:
  BIOME_LOG_PATH:               unset
  BIOME_LOG_PREFIX_NAME:        unset
  BIOME_CONFIG_PATH:            unset
  NO_COLOR:                     unset
  TERM:                         "tmux-256color"
  JS_RUNTIME_VERSION:           unset
  JS_RUNTIME_NAME:              unset
  NODE_PACKAGE_MANAGER:         unset

Biome Configuration:
  Status:                       Loaded successfully
  Formatter disabled:           false
  Linter disabled:              false
  Organize imports disabled:    false
  VCS disabled:                 false

Formatter:
  Format with errors:           false
  Indent style:                 Tab
  Indent width:                 2
  Line ending:                  Lf
  Line width:                   80
  Attribute position:           Auto
  Bracket spacing:              BracketSpacing(true)
  Ignore:                       ["configuration_schema.json"]
  Include:                      []

JavaScript Formatter:
  Enabled:                      true
  JSX quote style:              Double
  Quote properties:             AsNeeded
  Trailing commas:              All
  Semicolons:                   Always
  Arrow parentheses:            Always
  Bracket spacing:              unset
  Bracket same line:            false
  Quote style:                  Double
  Indent style:                 unset
  Indent width:                 unset
  Line ending:                  unset
  Line width:                   unset
  Attribute position:           unset

JSON Formatter:
  Enabled:                      false
  Indent style:                 Space
  Indent width:                 unset
  Line ending:                  unset
  Line width:                   1
  Trailing Commas:              unset

CSS Formatter:
  Enabled:                      true
  Indent style:                 unset
  Indent width:                 unset
  Line ending:                  unset
  Line width:                   unset
  Quote style:                  Double

GraphQL Formatter:
  Enabled:                      false
  Indent style:                 unset
  Indent width:                 unset
  Line ending:                  unset
  Line width:                   unset
  Bracket spacing:              unset
  Quote style:                  unset

Workspace:
  Open Documents:               0
```

### Configuration

```JSON

```

### Playground link

https://biomejs.dev/playground/?indentStyle=space&indentWidth=4&bracketSameLine=true&files.main.html=PABkAGkAdgAgAGEAdAB0AHIAPQBpAC0AYQBtAC0AbwBrAD4APAAvAGQAaQB2AD4ACgA8AGQAaQB2ACAAYQB0AHQAcgA9AGkALQBoAGEAdgBlAC0AYQBuAC0AZQB4AHQAcgBhAC0AcwBwAGEAYwBlACAAYQBuAG8AdABoAGUAcgBfAGEAdAB0AHIAPgA8AC8AZABpAHYAPgAKADwAZABpAHYAIABhAHQAdAByAD0AaQAtAGgAYQB2AGUALQBtAHUAbAB0AGkAcABsAGUALQBlAHgAdAByAGEALQBzAHAAYQBjAGUAcwAgACAAIAAgACAAYQBuAG8AdABoAGUAcgBfAGEAdAB0AHIAPgA8AC8AZABpAHYAPgAKADwAZABpAHYAIABhAHQAdAByAD0AaQAtAGEAbABzAG8ALQBoAGEAdgBlAC0AYQBuAC0AZQB4AHQAcgBhAC0AcwBwAGEAYwBlACAAPgA8AC8AZABpAHYAPgAKAA%3D%3D

### Code of Conduct

- [x] I agree to follow Biome's Code of Conduct


Discussion / hints:
Hi! Thank you for biome! I am really happy at how fast it is. I have a bug report that I came across while I was experimenting with the early experimental-html formatter. I looked around some of the existing Github issues and did not find an already existing report for this though please feel free to mark it as a duplicate in case I missed something.

The bug is that the below segment:

```html
<div attr=i-am-ok></div>
<div attr=i-have-an-extra-space another_attr></div>
<div attr=i-have-multiple-extra-spaces     another_attr></div>
<div attr=i-also-have-an-extra-space ></div>
```

gets formatted into:

```html
<div attr="i-am-ok"></div>
<div attr="i-have-an-extra-space " another_attr></div>
<div attr="i-have-multiple-extra-spaces     " another_attr></div>
<div attr="i-also-have-an-extra-space "></div>
```

note the additional whitespace after some of the semi-colons that change the the attribute value strings to have extra trailing whitespace.

I have included a playground link [here](https://biomejs.dev/playground/?indentStyle=space&indentWidth=4&bracketSameLine=true&files.main.html=PABkAGkAdgAgAGEAdAB0AHIAPQBpAC0AYQBtAC0AbwBrAD4APAAvAGQAaQB2AD4ACgA8AGQAaQB2ACAAYQB0AHQAcgA9AGkALQBoAGEAdgBlAC0AYQBuAC0AZQB4AHQAcgBhAC0AcwBwAGEAYwBlACAAYQBuAG8AdABoAGUAcgBfAGEAdAB0AHIAPgA8AC8AZABpAHYAPgAKADwAZABpAHYAIABhAHQAdAByAD0AaQAtAGgAYQB2AGUALQBtAHUAbAB0AGkAcABsAGUALQBlAHgAdAByAGEALQBzAHAAYQBjAGUAcwAgACAAIAAgACAAYQBuAG8AdABoAGUAcgBfAGEAdAB0AHIAPgA8AC8AZABpAHYAPgAKADwAZABpAHYAIABhAHQAdAByAD0AaQAtAGEAbABzAG8ALQBoAGEAdgBlAC0AYQBuAC0AZQB4AHQAcgBhAC0AcwBwAGEAYwBlACAAPgA8AC8AZABpAHYAPgAKAA%3D%3D) that reproduces the issue.

I am happy to answer any questions you may have/provide additional context.


Fix patch:
diff --git a/crates/biome_html_formatter/src/html/auxiliary/string.rs b/crates/biome_html_formatter/src/html/auxiliary/string.rs
index c955b68c10ad..865be5d320ca 100644
--- a/crates/biome_html_formatter/src/html/auxiliary/string.rs
+++ b/crates/biome_html_formatter/src/html/auxiliary/string.rs
@@ -8,8 +8,9 @@ impl FormatNodeRule<HtmlString> for FormatHtmlString {
         let HtmlStringFields { value_token } = node.as_fields();
 
         // Prettier always uses double quotes for HTML strings, regardless of configuration.
+        // Unless the string contains a double quote, in which case it uses single quotes.
         if let Ok(value) = value_token.as_ref() {
-            let value_text = value.text().trim();
+            let value_text = value.text_trimmed();
 
             if !(value_text.starts_with('"') && value_text.ends_with('"')) {
                 let contains_double_quote = value_text.contains('"');
@@ -18,9 +19,12 @@ impl FormatNodeRule<HtmlString> for FormatHtmlString {
                     && value_text.ends_with('\'')
                     && !contains_double_quote
                 {
-                    value.text_range().add_start(1.into()).sub_end(1.into())
+                    value
+                        .text_trimmed_range()
+                        .add_start(1.into())
+                        .sub_end(1.into())
                 } else {
-                    value.text_range()
+                    value.text_trimmed_range()
                 };
 
                 if !contains_double_quote {


Test patch:
diff --git a/crates/biome_html_formatter/tests/specs/html/attributes/single-quotes.html.snap b/crates/biome_html_formatter/tests/specs/html/attributes/single-quotes.html.snap
index b6ad62a0073c..57d440d4bcae 100644
--- a/crates/biome_html_formatter/tests/specs/html/attributes/single-quotes.html.snap
+++ b/crates/biome_html_formatter/tests/specs/html/attributes/single-quotes.html.snap
@@ -28,5 +28,5 @@ Indent script and style: false
 -----
 
 ```html
-<img src="foo.png'" alt='should keep "these" quotes' />
+<img src="foo.png" alt='should keep "these" quotes' />
 ```
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/attributes/attributes.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/attributes/attributes.html.snap
index 34fe5d686ac5..0f2884e263d9 100644
--- a/crates/biome_html_formatter/tests/specs/prettier/html/attributes/attributes.html.snap
+++ b/crates/biome_html_formatter/tests/specs/prettier/html/attributes/attributes.html.snap
@@ -90,29 +90,6 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS.">
 ```diff
 --- Prettier
 +++ Biome
-@@ -1,6 +1,6 @@
--<input name="address" maxlength="200" />
-+<input name="address " maxlength="200" />
-+<input name="address'" maxlength="200" />
- <input name="address" maxlength="200" />
--<input name="address" maxlength="200" />
- <div class="foo"></div>
- <div class="foo"></div>
- <div class="foo bar"></div>
-@@ -18,11 +18,11 @@
- <div attribute="">String</div>
- <div attribute="">String</div>
- <div attribute>String</div>
--<div attribute="">String</div>
- <div attribute="">String</div>
-+<div attribute="'">String</div>
- <div attribute>String</div>
- <div attribute="">String</div>
--<div attribute="">String</div>
-+<div attribute="' ">String</div>
- <div attribute="attribute = attribute"></div>
- <div ATTRIBUTE>String</div>
- <div ATTRIBUTE="">String</div>
 @@ -61,8 +61,14 @@
    data-index-number="12314"
    data-parent="cars"
@@ -138,8 +115,8 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS.">
 # Output
 
 ```html
-<input name="address " maxlength="200" />
-<input name="address'" maxlength="200" />
+<input name="address" maxlength="200" />
+<input name="address" maxlength="200" />
 <input name="address" maxlength="200" />
 <div class="foo"></div>
 <div class="foo"></div>
@@ -159,10 +136,10 @@ and HTML5 Apps. It also documents Mozilla products, like Firefox OS."
 <div attribute="">String</div>
 <div attribute>String</div>
 <div attribute="">String</div>
-<div attribute="'">String</div>
+<div attribute="">String</div>
 <div attribute>String</div>
 <div attribute="">String</div>
-<div attribute="' ">String</div>
+<div attribute="">String</div>
 <div attribute="attribute = attribute"></div>
 <div ATTRIBUTE>String</div>
 <div ATTRIBUTE="">String</div>
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/attributes/boolean.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/attributes/boolean.html.snap
deleted file mode 100644
index 91e1157605c4..000000000000
--- a/crates/biome_html_formatter/tests/specs/prettier/html/attributes/boolean.html.snap
+++ /dev/null
@@ -1,81 +0,0 @@
----
-source: crates/biome_formatter_test/src/snapshot_builder.rs
-info: html/attributes/boolean.html
----
-# Input
-
-```html
-<button type="submit">This is valid.</button>
-<button type="submit" disabled>This is valid.</button>
-<button type="submit" disabled="">This is valid.</button>
-<button type="submit" disabled="disabled">This is valid.</button>
-<button type="submit" disabled=true>This is valid. This will be disabled.</button>
-<button type="submit" disabled='true'>This is valid. This will be disabled.</button>
-<button type="submit" disabled="true">This is valid. This will be disabled.</button>
-<button type="submit" disabled=false>This is valid. This will be disabled.</button>
-<button type="submit" disabled="false">This is valid. This will be disabled.</button>
-<button type="submit" disabled='false'>This is valid. This will be disabled.</button>
-<button type="submit" disabled=hahah>This is valid. This will be disabled.</button>
-<button type="submit" disabled='hahah'>This is valid. This will be disabled.</button>
-<button type="submit" disabled="hahah">This is valid. This will be disabled.</button>
-<input type="checkbox" checked disabled name="cheese">
-<input type="checkbox" checked="checked" disabled="disabled" name="cheese">
-<input type='checkbox' checked="" disabled="" name=cheese >
-<div lang=""></div>
-
-```
-
-
-# Prettier differences
-
-```diff
---- Prettier
-+++ Biome
-@@ -31,5 +31,5 @@
- </button>
- <input type="checkbox" checked disabled name="cheese" />
- <input type="checkbox" checked="checked" disabled="disabled" name="cheese" />
--<input type="checkbox" checked="" disabled="" name="cheese" />
-+<input type="checkbox'" checked="" disabled="" name="cheese " />
- <div lang=""></div>
-```
-
-# Output
-
-```html
-<button type="submit">This is valid.</button>
-<button type="submit" disabled>This is valid.</button>
-<button type="submit" disabled="">This is valid.</button>
-<button type="submit" disabled="disabled">This is valid.</button>
-<button type="submit" disabled="true">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="true">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="true">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="false">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="false">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="false">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="hahah">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="hahah">
-  This is valid. This will be disabled.
-</button>
-<button type="submit" disabled="hahah">
-  This is valid. This will be disabled.
-</button>
-<input type="checkbox" checked disabled name="cheese" />
-<input type="checkbox" checked="checked" disabled="disabled" name="cheese" />
-<input type="checkbox'" checked="" disabled="" name="cheese " />
-<div lang=""></div>
-```
diff --git a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap
index f6223410fb79..5b3ce8e67da1 100644
--- a/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap
+++ b/crates/biome_html_formatter/tests/specs/prettier/html/whitespace/display-inline-block.html.snap
@@ -40,15 +40,6 @@ Click here! Click here! Click here! Click here! Click here! Click here!
  </div>
  <div>
    <button>
-@@ -20,6 +21,6 @@
-   </button>
- </div>
- <video src="brave.webm">
--  <track kind="subtitles" src="brave.en.vtt" srclang="en" label="English" />
--  <track kind="subtitles" src="brave.en.vtt" srclang="en" label="English" />
-+  <track kind="subtitles " src="brave.en.vtt " srclang="en " label="English" />
-+  <track kind="subtitles " src="brave.en.vtt " srclang="en " label="English" />
- </video>
 ```
 
 # Output
@@ -77,7 +68,7 @@ Click here! Click here! Click here! Click here! Click here! Click here!
   </button>
 </div>
 <video src="brave.webm">
-  <track kind="subtitles " src="brave.en.vtt " srclang="en " label="English" />
-  <track kind="subtitles " src="brave.en.vtt " srclang="en " label="English" />
+  <track kind="subtitles" src="brave.en.vtt" srclang="en" label="English" />
+  <track kind="subtitles" src="brave.en.vtt" srclang="en" label="English" />
 </video>
 ```