Skip to content

Commit

Permalink
feat(css-parser): CSS Parser: parse starting-style at rule #1478 (#1518)
Browse files Browse the repository at this point in the history
  • Loading branch information
denbezrukov authored Jan 11, 2024
1 parent e65764b commit 2cd6eaf
Show file tree
Hide file tree
Showing 24 changed files with 826 additions and 38 deletions.
12 changes: 12 additions & 0 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.

26 changes: 26 additions & 0 deletions 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/any/at_rule.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ impl FormatRule<AnyCssAtRule> for FormatAnyCssAtRule {
AnyCssAtRule::CssScopeAtRule(node) => node.format().fmt(f),
AnyCssAtRule::CssImportAtRule(node) => node.format().fmt(f),
AnyCssAtRule::CssNamespaceAtRule(node) => node.format().fmt(f),
AnyCssAtRule::CssStartingStyleAtRule(node) => node.format().fmt(f),
AnyCssAtRule::CssBogusAtRule(node) => node.format().fmt(f),
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/biome_css_formatter/src/css/any/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ pub(crate) mod rule_list_block;
pub(crate) mod scope_range;
pub(crate) mod selector;
pub(crate) mod simple_selector;
pub(crate) mod starting_style_block;
pub(crate) mod sub_selector;
pub(crate) mod supports_and_combinable_condition;
pub(crate) mod supports_condition;
Expand Down
16 changes: 16 additions & 0 deletions crates/biome_css_formatter/src/css/any/starting_style_block.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file.
use crate::prelude::*;
use biome_css_syntax::AnyCssStartingStyleBlock;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatAnyCssStartingStyleBlock;
impl FormatRule<AnyCssStartingStyleBlock> for FormatAnyCssStartingStyleBlock {
type Context = CssFormatContext;
fn fmt(&self, node: &AnyCssStartingStyleBlock, f: &mut CssFormatter) -> FormatResult<()> {
match node {
AnyCssStartingStyleBlock::CssRuleListBlock(node) => node.format().fmt(f),
AnyCssStartingStyleBlock::CssDeclarationListBlock(node) => node.format().fmt(f),
AnyCssStartingStyleBlock::CssBogusBlock(node) => node.format().fmt(f),
}
}
}
1 change: 1 addition & 0 deletions crates/biome_css_formatter/src/css/statements/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ pub(crate) mod media_at_rule;
pub(crate) mod namespace_at_rule;
pub(crate) mod page_at_rule;
pub(crate) mod scope_at_rule;
pub(crate) mod starting_style_at_rule;
pub(crate) mod supports_at_rule;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::prelude::*;
use biome_css_syntax::CssStartingStyleAtRule;
use biome_rowan::AstNode;
#[derive(Debug, Clone, Default)]
pub(crate) struct FormatCssStartingStyleAtRule;
impl FormatNodeRule<CssStartingStyleAtRule> for FormatCssStartingStyleAtRule {
fn fmt_fields(&self, node: &CssStartingStyleAtRule, f: &mut CssFormatter) -> FormatResult<()> {
format_verbatim_node(node.syntax()).fmt(f)
}
}
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 @@ -2680,6 +2680,46 @@ impl IntoFormat<CssFormatContext> for biome_css_syntax::CssNamespaceAtRule {
)
}
}
impl FormatRule<biome_css_syntax::CssStartingStyleAtRule>
for crate::css::statements::starting_style_at_rule::FormatCssStartingStyleAtRule
{
type Context = CssFormatContext;
#[inline(always)]
fn fmt(
&self,
node: &biome_css_syntax::CssStartingStyleAtRule,
f: &mut CssFormatter,
) -> FormatResult<()> {
FormatNodeRule::<biome_css_syntax::CssStartingStyleAtRule>::fmt(self, node, f)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::CssStartingStyleAtRule {
type Format<'a> = FormatRefWithRule<
'a,
biome_css_syntax::CssStartingStyleAtRule,
crate::css::statements::starting_style_at_rule::FormatCssStartingStyleAtRule,
>;
fn format(&self) -> Self::Format<'_> {
#![allow(clippy::default_constructed_unit_structs)]
FormatRefWithRule::new(
self,
crate::css::statements::starting_style_at_rule::FormatCssStartingStyleAtRule::default(),
)
}
}
impl IntoFormat<CssFormatContext> for biome_css_syntax::CssStartingStyleAtRule {
type Format = FormatOwnedWithRule<
biome_css_syntax::CssStartingStyleAtRule,
crate::css::statements::starting_style_at_rule::FormatCssStartingStyleAtRule,
>;
fn into_format(self) -> Self::Format {
#![allow(clippy::default_constructed_unit_structs)]
FormatOwnedWithRule::new(
self,
crate::css::statements::starting_style_at_rule::FormatCssStartingStyleAtRule::default(),
)
}
}
impl FormatRule<biome_css_syntax::CssContainerNotQuery>
for crate::css::auxiliary::container_not_query::FormatCssContainerNotQuery
{
Expand Down Expand Up @@ -7621,6 +7661,33 @@ impl IntoFormat<CssFormatContext> for biome_css_syntax::AnyCssNamespaceUrl {
)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::AnyCssStartingStyleBlock {
type Format<'a> = FormatRefWithRule<
'a,
biome_css_syntax::AnyCssStartingStyleBlock,
crate::css::any::starting_style_block::FormatAnyCssStartingStyleBlock,
>;
fn format(&self) -> Self::Format<'_> {
#![allow(clippy::default_constructed_unit_structs)]
FormatRefWithRule::new(
self,
crate::css::any::starting_style_block::FormatAnyCssStartingStyleBlock::default(),
)
}
}
impl IntoFormat<CssFormatContext> for biome_css_syntax::AnyCssStartingStyleBlock {
type Format = FormatOwnedWithRule<
biome_css_syntax::AnyCssStartingStyleBlock,
crate::css::any::starting_style_block::FormatAnyCssStartingStyleBlock,
>;
fn into_format(self) -> Self::Format {
#![allow(clippy::default_constructed_unit_structs)]
FormatOwnedWithRule::new(
self,
crate::css::any::starting_style_block::FormatAnyCssStartingStyleBlock::default(),
)
}
}
impl AsFormat<CssFormatContext> for biome_css_syntax::AnyCssUrlValue {
type Format<'a> = FormatRefWithRule<
'a,
Expand Down
1 change: 1 addition & 0 deletions crates/biome_css_parser/src/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -997,6 +997,7 @@ impl<'src> CssLexer<'src> {
b"scope" => SCOPE_KW,
b"import" => IMPORT_KW,
b"namespace" => NAMESPACE_KW,
b"starting-style" => STARTING_STYLE_KW,
_ => IDENT,
}
}
Expand Down
10 changes: 10 additions & 0 deletions crates/biome_css_parser/src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,22 @@ pub(crate) struct CssParserState {
/// The challenge is, that it isn't possible to tell which of the two kinds it is until the parser
/// processed all of `(a, b)`.
pub(crate) speculative_parsing: bool,

/// Indicates whether the parser is currently dealing with a nesting block in the CSS document.
///
/// This field is essential for understanding the current context of the parser. When set to `true`,
/// it indicates that the parser is inside a nested or inner element, such as rules within media queries
/// or other nested structures. Conversely, when set to `false`, it implies that the parser is at the root level,
/// handling top-level `@rules` or style declarations directly under the stylesheet.
/// This distinction is critical for correctly interpreting and parsing different sections of a CSS document.
pub(crate) is_nesting_block: bool,
}

impl CssParserState {
pub fn new() -> Self {
Self {
speculative_parsing: false,
is_nesting_block: false,
}
}
}
11 changes: 5 additions & 6 deletions crates/biome_css_parser/src/syntax/at_rule/keyframes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::parser::CssParser;
use crate::syntax::at_rule::parse_error::{
expected_keyframes_item, expected_keyframes_item_selector,
};
use crate::syntax::blocks::parse_declaration_list_block;
use crate::syntax::blocks::{parse_block_body, parse_declaration_list_block};
use crate::syntax::css_dimension::{is_at_percentage_dimension, parse_percentage_dimension};
use crate::syntax::parse_error::{expected_block, expected_non_css_wide_keyword_identifier};
use crate::syntax::{
Expand Down Expand Up @@ -74,10 +74,9 @@ fn parse_keyframes_block(p: &mut CssParser) -> ParsedSyntax {
return Absent;
}

let m = p.start();
p.bump(T!['{']);
KeyframesItemList.parse_list(p);
p.expect(T!['}']);
let m = parse_block_body(p, |p| {
KeyframesItemList.parse_list(p);
});

Present(m.complete(p, CSS_KEYFRAMES_BLOCK))
}
Expand Down Expand Up @@ -141,7 +140,7 @@ impl ParseRecovery for KeyframesItemBlockParseRecovery {
// color: blue;
// }
// }
p.at_ts(token_set!(T!['}'])) || is_at_keyframes_item_selector(p)
p.at(T!['}']) || is_at_keyframes_item_selector(p)
}
}

Expand Down
6 changes: 6 additions & 0 deletions crates/biome_css_parser/src/syntax/at_rule/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod namespace;
mod page;
mod parse_error;
mod scope;
mod starting_style;
mod supports;

use crate::parser::CssParser;
Expand All @@ -35,6 +36,9 @@ use crate::syntax::at_rule::media::{is_at_media_at_rule, parse_media_at_rule};
use crate::syntax::at_rule::namespace::{is_at_namespace_at_rule, parse_namespace_at_rule};
use crate::syntax::at_rule::page::{is_at_page_at_rule, parse_page_at_rule};
use crate::syntax::at_rule::scope::{is_at_scope_at_rule, parse_scope_at_rule};
use crate::syntax::at_rule::starting_style::{
is_at_starting_style_at_rule, parse_starting_style_at_rule,
};
use crate::syntax::at_rule::supports::{is_at_supports_at_rule, parse_supports_at_rule};
use crate::syntax::parse_error::expected_any_at_rule;
use biome_css_syntax::CssSyntaxKind::*;
Expand Down Expand Up @@ -99,6 +103,8 @@ pub(crate) fn parse_any_at_rule(p: &mut CssParser) -> ParsedSyntax {
parse_import_at_rule(p)
} else if is_at_namespace_at_rule(p) {
parse_namespace_at_rule(p)
} else if is_at_starting_style_at_rule(p) {
parse_starting_style_at_rule(p)
} else {
Absent
}
Expand Down
9 changes: 4 additions & 5 deletions crates/biome_css_parser/src/syntax/at_rule/page.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::syntax::at_rule::parse_error::{
expected_any_page_at_rule_item, expected_page_selector, expected_page_selector_pseudo,
};
use crate::syntax::at_rule::{is_at_at_rule, parse_at_rule};
use crate::syntax::blocks::parse_or_recover_declaration_or_rule_list_block;
use crate::syntax::blocks::{parse_block_body, parse_or_recover_declaration_or_rule_list_block};
use crate::syntax::parse_error::expected_block;
use crate::syntax::{
is_at_identifier, parse_custom_identifier_with_keywords, parse_declaration_with_semicolon,
Expand Down Expand Up @@ -176,11 +176,10 @@ pub(crate) fn parse_page_block(p: &mut CssParser) -> ParsedSyntax {
if !p.at(T!['{']) {
return Absent;
}
let m = p.start();

p.expect(T!['{']);
PageAtRuleItemList.parse_list(p);
p.expect(T!['}']);
let m = parse_block_body(p, |p| {
PageAtRuleItemList.parse_list(p);
});

Present(m.complete(p, CSS_PAGE_AT_RULE_BLOCK))
}
Expand Down
65 changes: 65 additions & 0 deletions crates/biome_css_parser/src/syntax/at_rule/starting_style.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::parser::CssParser;
use crate::syntax::blocks::{
parse_or_recover_declaration_list_block, parse_or_recover_rule_list_block,
};
use biome_css_syntax::CssSyntaxKind::*;
use biome_css_syntax::T;
use biome_parser::parsed_syntax::ParsedSyntax::Present;
use biome_parser::prelude::ParsedSyntax::Absent;
use biome_parser::prelude::*;

/// Checks if the current token in the parser is a `@starting-style` at-rule.
///
/// This function verifies if the current token matches the `@starting-style` rule,
/// which is a custom at-rule used for specific parsing scenarios.
#[inline]
pub(crate) fn is_at_starting_style_at_rule(p: &mut CssParser) -> bool {
p.at(T![starting_style])
}

/// Parses a `@starting-style` at-rule in a CSS stylesheet.
///
/// This function handles the parsing of a `@starting-style` at-rule, which is defined in the
/// CSS Transitions Level 2 specification. It starts by confirming the presence of such a rule and then
/// processes the content depending on whether the parser is currently inside a nesting block or at the root level.
/// It employs different parsing strategies for declarations or rules based on the parser state `is_nesting_block`.
///
/// Specification: [CSS Transitions Level 2 - @starting-style](https://drafts.csswg.org/css-transitions-2/#at-ruledef-starting-style)
/// # Examples
/// Basic usage in a CSS stylesheet:
///
/// ```css
/// // At the root level of a stylesheet
/// @starting-style {
/// /* rulesets */
/// }
///
/// // Inside a selector
/// selector {
/// @starting-style {
/// /* declarations */
/// }
/// }
/// ```
#[inline]
pub(crate) fn parse_starting_style_at_rule(p: &mut CssParser) -> ParsedSyntax {
if !is_at_starting_style_at_rule(p) {
return Absent;
}

let m = p.start();

p.bump(T![starting_style]);

let block = if p.state().is_nesting_block {
parse_or_recover_declaration_list_block(p)
} else {
parse_or_recover_rule_list_block(p)
};

if block.is_err() {
return Present(m.complete(p, CSS_BOGUS_AT_RULE));
}

Present(m.complete(p, CSS_STARTING_STYLE_AT_RULE))
}
Loading

0 comments on commit 2cd6eaf

Please sign in to comment.