Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .changeset/plenty-actors-think.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@biomejs/biome": patch
---

Fixed [#4298](https://github.com/biomejs/biome/issues/4298). Biome now correctly formats CSS declarations when it contains one single value:

```diff
.bar {
- --123456789012345678901234567890: var(--1234567890123456789012345678901234567);
+ --123456789012345678901234567890: var(
+ --1234567890123456789012345678901234567
+ );
}
```
122 changes: 66 additions & 56 deletions crates/biome_css_formatter/src/utils/component_value_list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,73 +30,83 @@ where
};

let values = format_with(|f: &mut Formatter<'_, CssFormatContext>| {
let mut fill = f.fill();
let mut at_group_boundary = false;
if node.len() == 1 {
let mut builder = f.join_nodes_with_soft_line();

for (element, formatted) in node.iter().zip(node.iter().formatted()) {
fill.entry(
&format_once(|f| {
// If the current element is not a comma, insert a soft line break or a space.
// Consider the CSS example: `font: first , second;`
// The desired format is: `font: first, second;`
// A separator should not be added before the comma because the comma acts as a `CssGenericDelimiter`.
let token_kind = CssGenericDelimiter::cast_ref(element.syntax())
.and_then(|node| node.value().ok())
.map(|token| token.kind());
for (element, formatted) in node.iter().zip(node.iter().formatted()) {
builder.entry(element.syntax(), &formatted);
}

let is_comma = matches!(token_kind, Some(CssSyntaxKind::COMMA));
builder.finish()
} else {
let mut fill = f.fill();
let mut at_group_boundary = false;

if !is_comma {
if matches!(
layout,
ValueListLayout::PreserveInline | ValueListLayout::OnePerLine
) {
let has_leading_newline = element.syntax().has_leading_newline();
for (element, formatted) in node.iter().zip(node.iter().formatted()) {
fill.entry(
&format_once(|f| {
// If the current element is not a comma, insert a soft line break or a space.
// Consider the CSS example: `font: first , second;`
// The desired format is: `font: first, second;`
// A separator should not be added before the comma because the comma acts as a `CssGenericDelimiter`.
let token_kind = CssGenericDelimiter::cast_ref(element.syntax())
.and_then(|node| node.value().ok())
.map(|token| token.kind());

if has_leading_newline {
let is_comma = matches!(token_kind, Some(CssSyntaxKind::COMMA));

if !is_comma {
if matches!(
layout,
ValueListLayout::PreserveInline | ValueListLayout::OnePerLine
) {
let has_leading_newline = element.syntax().has_leading_newline();

if has_leading_newline {
write!(f, [hard_line_break()])?;
} else {
write!(f, [space()])?;
}
} else if at_group_boundary {
write!(f, [hard_line_break()])?;
} else {
write!(f, [space()])?;
write!(f, [soft_line_break_or_space()])?
}
} else if at_group_boundary {
write!(f, [hard_line_break()])?;
} else {
write!(f, [soft_line_break_or_space()])?
}
}

// If the layout is OneGroupPerLine, insert a hard line break as a `separator`
// between two adjacent groups.
//
// Consider the CSS example: `font: group one, group_two, group 3;`
// The desired format is:
// font:
// group one,
// group_two,
// group 3;
//
// A hard line break is inserted between:
// 1. `group one,` and `group_two,`
// 2. `group_two,` and `group 3;`
//
// Caveat:
// We also need to add a hard line break before the first group,
// but `FillBuilder.entry` will ignore any `separator` for the first item in the list,
// To address this, we prepend the hard line break after composing `values`.
//
// This is also why `at_group_boundary` is initialized to `false` even when
// the layout is OneGroupPerLine: because the line break would be ignored
// if `at_group_boundary` were set to `true` initially.
at_group_boundary =
is_comma && matches!(layout, ValueListLayout::OneGroupPerLine);
// If the layout is OneGroupPerLine, insert a hard line break as a `separator`
// between two adjacent groups.
//
// Consider the CSS example: `font: group one, group_two, group 3;`
// The desired format is:
// font:
// group one,
// group_two,
// group 3;
//
// A hard line break is inserted between:
// 1. `group one,` and `group_two,`
// 2. `group_two,` and `group 3;`
//
// Caveat:
// We also need to add a hard line break before the first group,
// but `FillBuilder.entry` will ignore any `separator` for the first item in the list,
// To address this, we prepend the hard line break after composing `values`.
//
// This is also why `at_group_boundary` is initialized to `false` even when
// the layout is OneGroupPerLine: because the line break would be ignored
// if `at_group_boundary` were set to `true` initially.
at_group_boundary =
is_comma && matches!(layout, ValueListLayout::OneGroupPerLine);

Ok(())
}),
&formatted,
);
}
Ok(())
}),
&formatted,
);
}

fill.finish()
fill.finish()
}
});

match layout {
Expand Down
8 changes: 8 additions & 0 deletions crates/biome_css_formatter/tests/specs/css/issue_4298.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.bar {
--123456789012345678901234567890: var(
--1234567890123456789012345678901234567
);
font: var(
--1234567890123456789012345678901234567
)
}
41 changes: 41 additions & 0 deletions crates/biome_css_formatter/tests/specs/css/issue_4298.css.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
source: crates/biome_formatter_test/src/snapshot_builder.rs
info: css/issue_4298.css
---
# Input

```css
.bar {
--123456789012345678901234567890: var(
--1234567890123456789012345678901234567
);
font: var(
--1234567890123456789012345678901234567
)
}

```


=============================

# Outputs

## Output 1

-----
Indent style: Tab
Indent width: 2
Line ending: LF
Line width: 80
Quote style: Double Quotes
-----

```css
.bar {
--123456789012345678901234567890: var(
--1234567890123456789012345678901234567
);
font: var(--1234567890123456789012345678901234567);
}
```