Skip to content

Commit

Permalink
fix(css_parser): fix CSS multiple ampersand support (#3367)
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov committed Jul 8, 2024
1 parent f2b49c0 commit f6ca506
Show file tree
Hide file tree
Showing 115 changed files with 3,691 additions and 2,707 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b
#### Bug fixes

- Fix [#3287](https://github.com/biomejs/biome/issues/3287) nested selectors with pseudo-classes. Contributed by @denbezrukov
- Fix [#3349](https://github.com/biomejs/biome/issues/3349) allow CSS multiple ampersand support. Contributed by @denbezrukov
```css
.class {
&& {
color: red;
}
}
```

## v1.8.3 (2024-06-27)

Expand Down
34 changes: 25 additions & 9 deletions crates/biome_css_factory/src/generated/node_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 23 additions & 1 deletion crates/biome_css_factory/src/generated/syntax_factory.rs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/biome_css_formatter/src/css/lists/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub(crate) mod keyframes_selector_list;
pub(crate) mod layer_name_list;
pub(crate) mod layer_reference_list;
pub(crate) mod media_query_list;
pub(crate) mod nested_selector_list;
pub(crate) mod page_at_rule_item_list;
pub(crate) mod page_selector_list;
pub(crate) mod page_selector_pseudo_list;
Expand Down
10 changes: 10 additions & 0 deletions crates/biome_css_formatter/src/css/lists/nested_selector_list.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::prelude::*;
use biome_css_syntax::CssNestedSelectorList;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssNestedSelectorList;
impl FormatRule<CssNestedSelectorList> for FormatCssNestedSelectorList {
type Context = CssFormatContext;
fn fmt(&self, node: &CssNestedSelectorList, f: &mut CssFormatter) -> FormatResult<()> {
f.join().entries(node.iter().formatted()).finish()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ pub(crate) struct FormatCssCompoundSelector;
impl FormatNodeRule<CssCompoundSelector> for FormatCssCompoundSelector {
fn fmt_fields(&self, node: &CssCompoundSelector, f: &mut CssFormatter) -> FormatResult<()> {
let CssCompoundSelectorFields {
nesting_selector_token,
nesting_selectors,
simple_selector,
sub_selectors,
} = node.as_fields();

write!(
f,
[group(&format_args![
nesting_selector_token.format(),
nesting_selectors.format(),
simple_selector.format(),
sub_selectors.format()
])]
Expand Down
1 change: 1 addition & 0 deletions crates/biome_css_formatter/src/css/selectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub(crate) mod compound_selector;
pub(crate) mod id_selector;
pub(crate) mod keyframes_ident_selector;
pub(crate) mod keyframes_percentage_selector;
pub(crate) mod nested_selector;
pub(crate) mod page_selector;
pub(crate) mod pseudo_class_function_compound_selector;
pub(crate) mod pseudo_class_function_selector;
Expand Down
11 changes: 11 additions & 0 deletions crates/biome_css_formatter/src/css/selectors/nested_selector.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
use crate::prelude::*;
use biome_css_syntax::{CssNestedSelector, CssNestedSelectorFields};
use biome_formatter::write;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssNestedSelector;
impl FormatNodeRule<CssNestedSelector> for FormatCssNestedSelector {
fn fmt_fields(&self, node: &CssNestedSelector, f: &mut CssFormatter) -> FormatResult<()> {
let CssNestedSelectorFields { amp_token } = node.as_fields();
write!(f, [amp_token.format()])
}
}
67 changes: 67 additions & 0 deletions crates/biome_css_formatter/src/generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3012,6 +3012,46 @@ impl IntoFormat<CssFormatContext> for biome_css_syntax::CssNestedQualifiedRule {
)
}
}
impl FormatRule<biome_css_syntax::CssNestedSelector>
for crate::css::selectors::nested_selector::FormatCssNestedSelector
{
type Context = CssFormatContext;
#[inline(always)]
fn fmt(
&self,
node: &biome_css_syntax::CssNestedSelector,
f: &mut CssFormatter,
) -> FormatResult<()> {
FormatNodeRule::<biome_css_syntax::CssNestedSelector>::fmt(self, node, f)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::CssNestedSelector {
type Format<'a> = FormatRefWithRule<
'a,
biome_css_syntax::CssNestedSelector,
crate::css::selectors::nested_selector::FormatCssNestedSelector,
>;
fn format(&self) -> Self::Format<'_> {
#![allow(clippy::default_constructed_unit_structs)]
FormatRefWithRule::new(
self,
crate::css::selectors::nested_selector::FormatCssNestedSelector::default(),
)
}
}
impl IntoFormat<CssFormatContext> for biome_css_syntax::CssNestedSelector {
type Format = FormatOwnedWithRule<
biome_css_syntax::CssNestedSelector,
crate::css::selectors::nested_selector::FormatCssNestedSelector,
>;
fn into_format(self) -> Self::Format {
#![allow(clippy::default_constructed_unit_structs)]
FormatOwnedWithRule::new(
self,
crate::css::selectors::nested_selector::FormatCssNestedSelector::default(),
)
}
}
impl FormatRule<biome_css_syntax::CssNthOffset>
for crate::css::auxiliary::nth_offset::FormatCssNthOffset
{
Expand Down Expand Up @@ -5991,6 +6031,33 @@ impl IntoFormat<CssFormatContext> for biome_css_syntax::CssMediaQueryList {
)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::CssNestedSelectorList {
type Format<'a> = FormatRefWithRule<
'a,
biome_css_syntax::CssNestedSelectorList,
crate::css::lists::nested_selector_list::FormatCssNestedSelectorList,
>;
fn format(&self) -> Self::Format<'_> {
#![allow(clippy::default_constructed_unit_structs)]
FormatRefWithRule::new(
self,
crate::css::lists::nested_selector_list::FormatCssNestedSelectorList::default(),
)
}
}
impl IntoFormat<CssFormatContext> for biome_css_syntax::CssNestedSelectorList {
type Format = FormatOwnedWithRule<
biome_css_syntax::CssNestedSelectorList,
crate::css::lists::nested_selector_list::FormatCssNestedSelectorList,
>;
fn into_format(self) -> Self::Format {
#![allow(clippy::default_constructed_unit_structs)]
FormatOwnedWithRule::new(
self,
crate::css::lists::nested_selector_list::FormatCssNestedSelectorList::default(),
)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::CssPageAtRuleItemList {
type Format<'a> = FormatRefWithRule<
'a,
Expand Down
25 changes: 25 additions & 0 deletions crates/biome_css_formatter/tests/specs/css/nesting/nesting_1.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
.test {
& {
color: pink;
}
&& {
color: pink;
}
& & {
color: pink;
}
div {
&&& {
color: pink;
}
& && {
color: pink;
}
&& & {
color: pink;
}
& & & {
color: pink;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
---
source: crates/biome_formatter_test/src/snapshot_builder.rs
info: css/nesting/nesting_1.css
---
# Input

```css
.test {
& {
color: pink;
}
&& {
color: pink;
}
& & {
color: pink;
}
div {
&&& {
color: pink;
}
& && {
color: pink;
}
&& & {
color: pink;
}
& & & {
color: pink;
}
}
}

```


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

# Outputs

## Output 1

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

```css
.test {
& {
color: pink;
}
&& {
color: pink;
}
& & {
color: pink;
}
div {
&&& {
color: pink;
}
& && {
color: pink;
}
&& & {
color: pink;
}
& & & {
color: pink;
}
}
}
```
5 changes: 3 additions & 2 deletions crates/biome_css_parser/src/syntax/selector/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
mod attribute;
mod nested_selector;
mod pseudo_class;
mod pseudo_element;
pub(crate) mod relative_selector;
Expand All @@ -9,6 +10,7 @@ use crate::syntax::parse_error::{
expected_any_sub_selector, expected_compound_selector, expected_identifier, expected_selector,
};
use crate::syntax::selector::attribute::parse_attribute_selector;
use crate::syntax::selector::nested_selector::NestedSelectorList;
use crate::syntax::selector::pseudo_class::parse_pseudo_class_selector;
use crate::syntax::selector::pseudo_element::parse_pseudo_element_selector;
use crate::syntax::{
Expand Down Expand Up @@ -285,8 +287,7 @@ fn parse_compound_selector(p: &mut CssParser) -> ParsedSyntax {

let m = p.start();

let context = selector_lex_context(p);
p.eat_with_context(T![&], context);
NestedSelectorList.parse_list(p);
parse_simple_selector(p).ok(); // We don't need to handle error here because a simple selector is optional
SubSelectorList.parse_list(p);

Expand Down
Loading

0 comments on commit f6ca506

Please sign in to comment.