diff --git a/crates/biome_css_factory/src/generated/node_factory.rs b/crates/biome_css_factory/src/generated/node_factory.rs index 4df74a975029..85e7108428d4 100644 --- a/crates/biome_css_factory/src/generated/node_factory.rs +++ b/crates/biome_css_factory/src/generated/node_factory.rs @@ -234,31 +234,28 @@ pub fn css_counter_style_at_rule( ], )) } -pub fn css_custom_property(value_token: SyntaxToken) -> CssCustomProperty { +pub fn css_custom_property(value: CssIdentifier) -> CssCustomProperty { CssCustomProperty::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_CUSTOM_PROPERTY, - [Some(SyntaxElement::Token(value_token))], + [Some(SyntaxElement::Node(value.into_syntax()))], )) } pub fn css_declaration( - name: CssIdentifier, - css_custom_property: CssCustomProperty, + name: AnyCssDeclarationName, colon_token: SyntaxToken, - value: AnyCssValue, + value: CssListOfComponentValues, ) -> CssDeclarationBuilder { CssDeclarationBuilder { name, - css_custom_property, colon_token, value, important: None, } } pub struct CssDeclarationBuilder { - name: CssIdentifier, - css_custom_property: CssCustomProperty, + name: AnyCssDeclarationName, colon_token: SyntaxToken, - value: AnyCssValue, + value: CssListOfComponentValues, important: Option, } impl CssDeclarationBuilder { @@ -271,7 +268,6 @@ impl CssDeclarationBuilder { CssSyntaxKind::CSS_DECLARATION, [ Some(SyntaxElement::Node(self.name.into_syntax())), - Some(SyntaxElement::Node(self.css_custom_property.into_syntax())), Some(SyntaxElement::Token(self.colon_token)), Some(SyntaxElement::Node(self.value.into_syntax())), self.important @@ -292,15 +288,6 @@ pub fn css_declaration_important( ], )) } -pub fn css_dimension(value: CssNumber, unit: CssIdentifier) -> CssDimension { - CssDimension::unwrap_cast(SyntaxNode::new_detached( - CssSyntaxKind::CSS_DIMENSION, - [ - Some(SyntaxElement::Node(value.into_syntax())), - Some(SyntaxElement::Node(unit.into_syntax())), - ], - )) -} pub fn css_id_selector(hash_token: SyntaxToken, name: CssIdentifier) -> CssIdSelector { CssIdSelector::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_ID_SELECTOR, @@ -600,10 +587,21 @@ pub fn css_number(value_token: SyntaxToken) -> CssNumber { [Some(SyntaxElement::Token(value_token))], )) } -pub fn css_parameter(any_css_value: AnyCssValue) -> CssParameter { +pub fn css_parameter(css_list_of_component_values: CssListOfComponentValues) -> CssParameter { CssParameter::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_PARAMETER, - [Some(SyntaxElement::Node(any_css_value.into_syntax()))], + [Some(SyntaxElement::Node( + css_list_of_component_values.into_syntax(), + ))], + )) +} +pub fn css_percent_dimension(value: CssNumber, unit_token: SyntaxToken) -> CssPercentDimension { + CssPercentDimension::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_PERCENT_DIMENSION, + [ + Some(SyntaxElement::Node(value.into_syntax())), + Some(SyntaxElement::Token(unit_token)), + ], )) } pub fn css_percentage(value: CssNumber, reminder_token: SyntaxToken) -> CssPercentage { @@ -923,15 +921,29 @@ pub fn css_pseudo_element_selector( ], )) } -pub fn css_ratio(numerator: CssNumber, denominator: CssNumber) -> CssRatio { +pub fn css_ratio( + numerator: CssNumber, + slash_token: SyntaxToken, + denominator: CssNumber, +) -> CssRatio { CssRatio::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_RATIO, [ Some(SyntaxElement::Node(numerator.into_syntax())), + Some(SyntaxElement::Token(slash_token)), Some(SyntaxElement::Node(denominator.into_syntax())), ], )) } +pub fn css_regular_dimension(value: CssNumber, unit: CssIdentifier) -> CssRegularDimension { + CssRegularDimension::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_REGULAR_DIMENSION, + [ + Some(SyntaxElement::Node(value.into_syntax())), + Some(SyntaxElement::Node(unit.into_syntax())), + ], + )) +} pub fn css_relative_selector(selector: AnyCssSelector) -> CssRelativeSelectorBuilder { CssRelativeSelectorBuilder { selector, @@ -1148,16 +1160,25 @@ where }), )) } -pub fn css_declaration_list(items: I) -> CssDeclarationList +pub fn css_declaration_list(items: I, separators: S) -> CssDeclarationList where I: IntoIterator, I::IntoIter: ExactSizeIterator, + S: IntoIterator, + S::IntoIter: ExactSizeIterator, { + let mut items = items.into_iter(); + let mut separators = separators.into_iter(); + let length = items.len() + separators.len(); CssDeclarationList::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_DECLARATION_LIST, - items - .into_iter() - .map(|item| Some(item.into_syntax().into())), + (0..length).map(|index| { + if index % 2 == 0 { + Some(items.next()?.into_syntax().into()) + } else { + Some(separators.next()?.into()) + } + }), )) } pub fn css_keyframes_item_list(items: I) -> CssKeyframesItemList @@ -1193,6 +1214,18 @@ where }), )) } +pub fn css_list_of_component_values(items: I) -> CssListOfComponentValues +where + I: IntoIterator, + I::IntoIter: ExactSizeIterator, +{ + CssListOfComponentValues::unwrap_cast(SyntaxNode::new_detached( + CssSyntaxKind::CSS_LIST_OF_COMPONENT_VALUES, + items + .into_iter() + .map(|item| Some(item.into_syntax().into())), + )) +} pub fn css_media_query_list(items: I, separators: S) -> CssMediaQueryList where I: IntoIterator, @@ -1214,16 +1247,25 @@ where }), )) } -pub fn css_parameter_list(items: I) -> CssParameterList +pub fn css_parameter_list(items: I, separators: S) -> CssParameterList where I: IntoIterator, I::IntoIter: ExactSizeIterator, + S: IntoIterator, + S::IntoIter: ExactSizeIterator, { + let mut items = items.into_iter(); + let mut separators = separators.into_iter(); + let length = items.len() + separators.len(); CssParameterList::unwrap_cast(SyntaxNode::new_detached( CssSyntaxKind::CSS_PARAMETER_LIST, - items - .into_iter() - .map(|item| Some(item.into_syntax().into())), + (0..length).map(|index| { + if index % 2 == 0 { + Some(items.next()?.into_syntax().into()) + } else { + Some(separators.next()?.into()) + } + }), )) } pub fn css_pseudo_value_list(items: I, separators: S) -> CssPseudoValueList diff --git a/crates/biome_css_factory/src/generated/syntax_factory.rs b/crates/biome_css_factory/src/generated/syntax_factory.rs index 83e69af39f8d..eea248db2d41 100644 --- a/crates/biome_css_factory/src/generated/syntax_factory.rs +++ b/crates/biome_css_factory/src/generated/syntax_factory.rs @@ -420,7 +420,7 @@ impl SyntaxFactory for CssSyntaxFactory { let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); let mut current_element = elements.next(); if let Some(element) = ¤t_element { - if element.kind() == CSS_CUSTOM_PROPERTY { + if CssIdentifier::can_cast(element.kind()) { slots.mark_present(); current_element = elements.next(); } @@ -436,17 +436,10 @@ impl SyntaxFactory for CssSyntaxFactory { } CSS_DECLARATION => { let mut elements = (&children).into_iter(); - let mut slots: RawNodeSlots<5usize> = RawNodeSlots::default(); + let mut slots: RawNodeSlots<4usize> = RawNodeSlots::default(); let mut current_element = elements.next(); if let Some(element) = ¤t_element { - if CssIdentifier::can_cast(element.kind()) { - slots.mark_present(); - current_element = elements.next(); - } - } - slots.next_slot(); - if let Some(element) = ¤t_element { - if CssCustomProperty::can_cast(element.kind()) { + if AnyCssDeclarationName::can_cast(element.kind()) { slots.mark_present(); current_element = elements.next(); } @@ -460,7 +453,7 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.next_slot(); if let Some(element) = ¤t_element { - if AnyCssValue::can_cast(element.kind()) { + if CssListOfComponentValues::can_cast(element.kind()) { slots.mark_present(); current_element = elements.next(); } @@ -507,32 +500,6 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_DECLARATION_IMPORTANT, children) } - CSS_DIMENSION => { - let mut elements = (&children).into_iter(); - let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); - let mut current_element = elements.next(); - if let Some(element) = ¤t_element { - if CssNumber::can_cast(element.kind()) { - slots.mark_present(); - current_element = elements.next(); - } - } - slots.next_slot(); - if let Some(element) = ¤t_element { - if CssIdentifier::can_cast(element.kind()) { - slots.mark_present(); - current_element = elements.next(); - } - } - slots.next_slot(); - if current_element.is_some() { - return RawSyntaxNode::new( - CSS_DIMENSION.to_bogus(), - children.into_iter().map(Some), - ); - } - slots.into_node(CSS_DIMENSION, children) - } CSS_ID_SELECTOR => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); @@ -1151,7 +1118,7 @@ impl SyntaxFactory for CssSyntaxFactory { let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); let mut current_element = elements.next(); if let Some(element) = ¤t_element { - if AnyCssValue::can_cast(element.kind()) { + if CssListOfComponentValues::can_cast(element.kind()) { slots.mark_present(); current_element = elements.next(); } @@ -1165,6 +1132,32 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_PARAMETER, children) } + CSS_PERCENT_DIMENSION => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if CssNumber::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T ! [%] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_PERCENT_DIMENSION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_PERCENT_DIMENSION, children) + } CSS_PERCENTAGE => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); @@ -1831,7 +1824,7 @@ impl SyntaxFactory for CssSyntaxFactory { } CSS_RATIO => { let mut elements = (&children).into_iter(); - let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); + let mut slots: RawNodeSlots<3usize> = RawNodeSlots::default(); let mut current_element = elements.next(); if let Some(element) = ¤t_element { if CssNumber::can_cast(element.kind()) { @@ -1840,6 +1833,13 @@ impl SyntaxFactory for CssSyntaxFactory { } } slots.next_slot(); + if let Some(element) = ¤t_element { + if element.kind() == T ! [/] { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); if let Some(element) = ¤t_element { if CssNumber::can_cast(element.kind()) { slots.mark_present(); @@ -1855,6 +1855,32 @@ impl SyntaxFactory for CssSyntaxFactory { } slots.into_node(CSS_RATIO, children) } + CSS_REGULAR_DIMENSION => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if CssNumber::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if let Some(element) = ¤t_element { + if CssIdentifier::can_cast(element.kind()) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + CSS_REGULAR_DIMENSION.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(CSS_REGULAR_DIMENSION, children) + } CSS_RELATIVE_SELECTOR => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); @@ -2144,9 +2170,13 @@ impl SyntaxFactory for CssSyntaxFactory { T ! [,], false, ), - CSS_DECLARATION_LIST => { - Self::make_node_list_syntax(kind, children, CssDeclaration::can_cast) - } + CSS_DECLARATION_LIST => Self::make_separated_list_syntax( + kind, + children, + CssDeclaration::can_cast, + T ! [;], + true, + ), CSS_KEYFRAMES_ITEM_LIST => { Self::make_node_list_syntax(kind, children, CssKeyframesBlock::can_cast) } @@ -2157,6 +2187,9 @@ impl SyntaxFactory for CssSyntaxFactory { T ! [,], false, ), + CSS_LIST_OF_COMPONENT_VALUES => { + Self::make_node_list_syntax(kind, children, AnyCssValue::can_cast) + } CSS_MEDIA_QUERY_LIST => Self::make_separated_list_syntax( kind, children, @@ -2164,9 +2197,13 @@ impl SyntaxFactory for CssSyntaxFactory { T ! [,], false, ), - CSS_PARAMETER_LIST => { - Self::make_node_list_syntax(kind, children, CssParameter::can_cast) - } + CSS_PARAMETER_LIST => Self::make_separated_list_syntax( + kind, + children, + CssParameter::can_cast, + T ! [,], + true, + ), CSS_PSEUDO_VALUE_LIST => Self::make_separated_list_syntax( kind, children, diff --git a/crates/biome_css_parser/src/lexer/mod.rs b/crates/biome_css_parser/src/lexer/mod.rs index 976ac34d9a6d..fedf32df8e04 100644 --- a/crates/biome_css_parser/src/lexer/mod.rs +++ b/crates/biome_css_parser/src/lexer/mod.rs @@ -496,6 +496,8 @@ impl<'src> CssLexer<'src> { TLD => self.consume_tilde(), PIP => self.consume_pipe(), EQL => self.consume_byte(T![=]), + EXL => self.consume_byte(T![!]), + PRC => self.consume_byte(T![%]), UNI => { // A BOM can only appear at the start of a file, so if we haven't advanced at all yet, @@ -944,7 +946,7 @@ impl<'src> CssLexer<'src> { COMMENT } - _ => self.consume_unexpected_character(), + _ => self.consume_byte(T![/]), } } diff --git a/crates/biome_css_parser/src/lexer/tests.rs b/crates/biome_css_parser/src/lexer/tests.rs index d86fa41c4bcf..3c50adf2ca6a 100644 --- a/crates/biome_css_parser/src/lexer/tests.rs +++ b/crates/biome_css_parser/src/lexer/tests.rs @@ -381,3 +381,20 @@ fn block_comment() { COMMENT:4 } } + + +#[test] +fn char() { + assert_lex! { + "!", + BANG:1 + } + assert_lex! { + "%", + PERCENT:1 + } + assert_lex! { + "/", + SLASH:1 + } +} diff --git a/crates/biome_css_parser/src/syntax/css_dimension.rs b/crates/biome_css_parser/src/syntax/css_dimension.rs new file mode 100644 index 000000000000..8a3a5ec17f15 --- /dev/null +++ b/crates/biome_css_parser/src/syntax/css_dimension.rs @@ -0,0 +1,110 @@ +use crate::parser::CssParser; +use biome_css_syntax::CssSyntaxKind::*; +use biome_css_syntax::T; +use biome_parser::prelude::ParsedSyntax; +use biome_parser::prelude::ParsedSyntax::{Absent, Present}; +use biome_parser::Parser; + +use super::parse_error::expected_unit; +use super::{parse_regular_identifier, parse_regular_number}; + +#[inline] +pub(crate) fn is_at_dimension(p: &mut CssParser) -> bool { + is_at_percentage_dimension(p) || is_at_regular_dimension(p) +} + +#[inline] +pub(crate) fn parse_dimension(p: &mut CssParser) -> ParsedSyntax { + if !is_at_dimension(p) { + return Absent; + } + if is_at_percentage_dimension(p) { + return parse_percentage_dimension(p); + } + parse_regular_dimension(p) +} + +fn is_at_percentage_dimension(p: &mut CssParser) -> bool { + p.at(CSS_NUMBER_LITERAL) && matches!(p.nth(1), T![%]) +} +#[inline] +fn parse_percentage_dimension(p: &mut CssParser) -> ParsedSyntax { + if !is_at_percentage_dimension(p) { + return Absent; + } + let m = p.start(); + parse_regular_number(p).ok(); + p.expect(T![%]); + Present(m.complete(p, CSS_PERCENTAGE)) +} +fn is_at_regular_dimension(p: &mut CssParser) -> bool { + p.at(CSS_NUMBER_LITERAL) && p.nth_at(1, T![ident]) +} +#[inline] +fn parse_regular_dimension(p: &mut CssParser) -> ParsedSyntax { + if !is_at_regular_dimension(p) { + return Absent; + } + let m = p.start(); + parse_regular_number(p).ok(); + parse_unit(p).or_add_diagnostic(p, expected_unit); + Present(m.complete(p, CSS_REGULAR_DIMENSION)) +} + +#[inline] +fn parse_unit(p: &mut CssParser) -> ParsedSyntax { + if !(p.at(T![ident]) && is_unit_str(p.cur_text())) { + return Absent; + } + parse_regular_identifier(p) +} + +#[inline] +fn is_unit_str(unit: &str) -> bool { + is_length_unit(unit) + || is_container_lengths_unit(unit) + || is_angle_unit(unit) + || is_time_unit(unit) + || is_frequency_unit(unit) + || is_resolution_unit(unit) + || is_flex_unit(unit) +} + +#[inline] +fn is_length_unit(unit: &str) -> bool { + matches!( + unit, + "em"| "rem"| "ex"| "rex"| "cap"| "rcap"| "ch"| "rch"| "ic"| "ric"| "lh"| "rlh"| + // Viewport-percentage Lengths + "vw"| "svw"| "lvw"| "dvw"| "vh"| "svh"| "lvh"| "dvh"| "vi"| "svi"| "lvi"| "dvi"| "vb"| + "svb"| "lvb"| "dvb"| "vmin"| "svmin"| "lvmin"| "dvmin"| "vmax"| "svmax"| "lvmax"| "dvmax"| + // Absolute lengths + "cm"| "mm"| "q"| "in"| "pc"| "pt"| "px"| "mozmm" | + // mini app + "rpx" + ) +} +#[inline] +fn is_container_lengths_unit(unit: &str) -> bool { + matches!(unit, "cqw" | "cqh" | "cqi" | "cqb" | "cqmin" | "cqmax") +} +#[inline] +fn is_angle_unit(unit: &str) -> bool { + matches!(unit, "deg" | "grad" | "rad" | "turn") +} +#[inline] +fn is_time_unit(unit: &str) -> bool { + matches!(unit, "s" | "ms") +} +#[inline] +fn is_frequency_unit(unit: &str) -> bool { + matches!(unit, "hz" | "khz") +} +#[inline] +fn is_resolution_unit(unit: &str) -> bool { + matches!(unit, "dpi" | "dpcm" | "dppx" | "x") +} +#[inline] +fn is_flex_unit(unit: &str) -> bool { + matches!(unit, "fr") +} diff --git a/crates/biome_css_parser/src/syntax/mod.rs b/crates/biome_css_parser/src/syntax/mod.rs index 94de5b16d231..b782f81ba40f 100644 --- a/crates/biome_css_parser/src/syntax/mod.rs +++ b/crates/biome_css_parser/src/syntax/mod.rs @@ -1,20 +1,25 @@ mod at_rule; +mod css_dimension; mod parse_error; mod selector; use crate::lexer::CssLexContext; use crate::parser::CssParser; use crate::syntax::at_rule::{at_at_rule, parse_at_rule}; +use crate::syntax::css_dimension::{is_at_dimension, parse_dimension}; use crate::syntax::parse_error::expected_block; +use crate::syntax::parse_error::expected_identifier; use crate::syntax::selector::CssSelectorList; use biome_css_syntax::CssSyntaxKind::*; use biome_css_syntax::{CssSyntaxKind, T}; -use biome_parser::parse_lists::ParseSeparatedList; +use biome_parser::parse_lists::{ParseNodeList, ParseSeparatedList}; use biome_parser::parse_recovery::{ParseRecovery, RecoveryResult}; use biome_parser::prelude::ParsedSyntax; use biome_parser::prelude::ParsedSyntax::{Absent, Present}; use biome_parser::{token_set, CompletedMarker, Parser, ParserProgress, TokenSet}; +use self::parse_error::{expected_component_value, expected_declaration_item, expected_number}; + const RULE_RECOVERY_SET: TokenSet = token_set![T![#], T![.], T![*], T![ident], T![:], T![::], T!['{']]; const SELECTOR_LIST_RECOVERY_SET: TokenSet = token_set![T!['{'], T!['}'],]; @@ -79,13 +84,242 @@ pub(crate) fn parse_rule_block(p: &mut CssParser) -> ParsedSyntax { } let m = p.start(); p.expect(T!['{']); - let list = p.start(); - list.complete(p, CSS_DECLARATION_LIST); + CssDeclarationList::default().parse_list(p); p.expect(T!['}']); Present(m.complete(p, CSS_BLOCK)) } +#[derive(Default)] +pub(crate) struct CssDeclarationList {} + +impl ParseSeparatedList for CssDeclarationList { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = CSS_DECLARATION_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_declaration_item(p) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T!['}']) + } + + fn allow_trailing_separating_element(&self) -> bool { + true + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> RecoveryResult { + parsed_element.or_recover( + p, + &ParseRecovery::new(CSS_BOGUS_DECLARATION_ITEM, token_set!(T!['}'])), + expected_declaration_item, + ) + } + + fn separating_element_kind(&mut self) -> Self::Kind { + T![;] + } +} + +#[derive(Default)] +struct ListOfComponentValues {} +impl ParseNodeList for ListOfComponentValues { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = CSS_LIST_OF_COMPONENT_VALUES; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_any_value(p) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + !is_any_value(p) + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> RecoveryResult { + parsed_element.or_recover( + p, + &ParseRecovery::new(CSS_BOGUS_COMPONENT_VALUE, token_set!(T!['}'], T![;])), + expected_component_value, + ) + } +} +#[inline] +pub(crate) fn parse_declaration_item(p: &mut CssParser) -> ParsedSyntax { + if !is_at_identifier(p) { + return Absent; + } + let m = p.start(); + parse_regular_identifier(p).ok(); + + p.expect(T![:]); + + ListOfComponentValues::default().parse_list(p); + + parse_declaration_important(p).ok(); + Present(m.complete(p, CSS_DECLARATION)) +} + +#[inline] +fn at_declaration_important(p: &mut CssParser) -> bool { + p.at(T![!]) && p.nth_at(1, T![important]) +} + +#[inline] +fn parse_declaration_important(p: &mut CssParser) -> ParsedSyntax { + if !at_declaration_important(p) { + return Absent; + } + let m = p.start(); + p.bump(T![!]); + p.bump(T![important]); + Present(m.complete(p, CSS_DECLARATION_IMPORTANT)) +} + +#[inline] +pub(crate) fn is_any_value(p: &mut CssParser) -> bool { + is_at_any_function(p) + || is_at_identifier(p) + || p.at(CSS_STRING_LITERAL) + || is_at_dimension(p) + || p.at(CSS_NUMBER_LITERAL) + || is_at_custom_property(p) + || is_at_ratio(p) +} + +#[inline] +pub(crate) fn parse_any_value(p: &mut CssParser) -> ParsedSyntax { + if is_at_any_function(p) { + parse_any_function(p) + } else if is_at_custom_property(p) { + parse_custom_property(p) + } else if is_at_identifier(p) { + parse_regular_identifier(p) + } else if p.at(CSS_STRING_LITERAL) { + parse_string(p) + } else if is_at_dimension(p) { + parse_dimension(p) + } else if is_at_ratio(p) { + parse_ratio(p) + } else if p.at(CSS_NUMBER_LITERAL) { + parse_regular_number(p) + } else { + Absent + } +} + +#[inline] +pub(crate) fn is_at_custom_property(p: &mut CssParser) -> bool { + is_at_identifier(p) && p.cur_text().starts_with("--") +} + +#[inline] +pub(crate) fn parse_custom_property(p: &mut CssParser) -> ParsedSyntax { + if is_at_custom_property(p) { + let m = p.start(); + parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier); + return Present(m.complete(p, CSS_CUSTOM_PROPERTY)); + } + Absent +} + +#[inline] +pub(crate) fn is_at_any_function(p: &mut CssParser) -> bool { + p.at(T![ident]) && p.nth_at(1, T!['(']) +} + +#[derive(Default)] +pub(crate) struct CssParameterList {} + +impl ParseSeparatedList for CssParameterList { + type Kind = CssSyntaxKind; + type Parser<'source> = CssParser<'source>; + const LIST_KIND: Self::Kind = CSS_PARAMETER_LIST; + + fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax { + parse_parameter(p) + } + + fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool { + p.at(T![')']) + } + + fn allow_trailing_separating_element(&self) -> bool { + true + } + + fn recover( + &mut self, + p: &mut Self::Parser<'_>, + parsed_element: ParsedSyntax, + ) -> RecoveryResult { + parsed_element.or_recover( + p, + &ParseRecovery::new(CSS_BOGUS_PARAMETER, token_set!(T![,], T![')'])), + expected_declaration_item, + ) + } + + fn separating_element_kind(&mut self) -> Self::Kind { + T![,] + } +} + +#[inline] +pub(crate) fn parse_parameter(p: &mut CssParser) -> ParsedSyntax { + if !is_any_value(p) { + return Absent; + } + let param = p.start(); + + ListOfComponentValues::default().parse_list(p); + + Present(param.complete(p, CSS_PARAMETER)) +} + +#[inline] +pub(crate) fn parse_any_function(p: &mut CssParser) -> ParsedSyntax { + if is_at_any_function(p) { + let m = p.start(); + let simple_fn = p.start(); + parse_regular_identifier(p).or_add_diagnostic(p, expected_identifier); + p.eat(T!['(']); + CssParameterList::default().parse_list(p); + p.expect(T![')']); + simple_fn.complete(p, CSS_SIMPLE_FUNCTION); + + return Present(m.complete(p, CSS_ANY_FUNCTION)); + } + Absent +} + +#[inline] +pub(crate) fn is_at_ratio(p: &mut CssParser) -> bool { + p.at(CSS_NUMBER_LITERAL) && p.nth_at(1, T![/]) +} + +#[inline] +pub(crate) fn parse_ratio(p: &mut CssParser) -> ParsedSyntax { + if !is_at_ratio(p) { + return Absent; + } + let m = p.start(); + parse_regular_number(p).ok(); + p.eat(T![/]); + parse_regular_number(p).or_add_diagnostic(p, expected_number); + Present(m.complete(p, CSS_RATIO)) +} + #[inline] pub(crate) fn is_at_identifier(p: &mut CssParser) -> bool { is_nth_at_identifier(p, 0) @@ -99,6 +333,7 @@ pub(crate) fn is_nth_at_identifier(p: &mut CssParser, n: usize) -> bool { pub(crate) fn parse_regular_identifier(p: &mut CssParser) -> ParsedSyntax { parse_identifier(p, CssLexContext::Regular) } + #[inline] pub(crate) fn parse_identifier(p: &mut CssParser, context: CssLexContext) -> ParsedSyntax { if !is_at_identifier(p) { diff --git a/crates/biome_css_parser/src/syntax/parse_error.rs b/crates/biome_css_parser/src/syntax/parse_error.rs index 2aab631f08d2..920f3b7a273f 100644 --- a/crates/biome_css_parser/src/syntax/parse_error.rs +++ b/crates/biome_css_parser/src/syntax/parse_error.rs @@ -164,3 +164,26 @@ pub(crate) fn expected_any_at_rule(p: &CssParser, range: TextRange) -> ParseDiag pub(crate) fn expected_block(p: &CssParser, range: TextRange) -> ParseDiagnostic { expected_node("body", range, p) } + +pub(crate) fn expected_declaration_item(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expected_node("declaration item", range, p) +} +pub(crate) fn expected_unit(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expected_node("unit", range, p) +} + +pub(crate) fn expected_component_value(p: &CssParser, range: TextRange) -> ParseDiagnostic { + expect_one_of( + &[ + "identifier", + "string", + "number", + "dimension", + "ratio", + "custom property", + "function", + ], + range, + ) + .into_diagnostic(p) +} diff --git a/crates/biome_css_parser/tests/css_test_suite/error/selector/invalid_selector.css.snap b/crates/biome_css_parser/tests/css_test_suite/error/selector/invalid_selector.css.snap index 73f3e0fee2a9..62a3ed2121c1 100644 --- a/crates/biome_css_parser/tests/css_test_suite/error/selector/invalid_selector.css.snap +++ b/crates/biome_css_parser/tests/css_test_suite/error/selector/invalid_selector.css.snap @@ -23,7 +23,7 @@ CssRoot { prelude: CssSelectorList [ CssBogusSelector { items: [ - ERROR_TOKEN@0..1 "!" [] [], + BANG@0..1 "!" [] [], ], }, missing separator, @@ -60,7 +60,7 @@ CssRoot { 0: CSS_RULE@0..15 0: CSS_SELECTOR_LIST@0..11 0: CSS_BOGUS_SELECTOR@0..1 - 0: ERROR_TOKEN@0..1 "!" [] [] + 0: BANG@0..1 "!" [] [] 1: (empty) 2: CSS_COMPOUND_SELECTOR@1..11 0: (empty) @@ -83,7 +83,14 @@ CssRoot { ``` invalid_selector.css:1:1 parse ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ - × unexpected character `!` + × Expected a selector but instead found '!'. + + > 1 │ !.selector { + │ ^ + 2 │ + 3 │ } + + i Expected a selector here. > 1 │ !.selector { │ ^ diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css new file mode 100644 index 000000000000..40982fa03319 --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css @@ -0,0 +1,33 @@ +a { + prop1: value; + prop2: value; +} + +a { + prop1: 1px; +} + +a { + prop1: a(); +} + +a { + prop1: a(1); +} + +a { + prop1: a(1,1); +} + +a { + prop1: a(1,1 1); +} + +a { + prop1: 2/3; +} + +a { + prop1: --custom; +} + diff --git a/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap new file mode 100644 index 000000000000..c1cea7827e9d --- /dev/null +++ b/crates/biome_css_parser/tests/css_test_suite/ok/declaration_list.css.snap @@ -0,0 +1,666 @@ +--- +source: crates/biome_css_parser/tests/spec_test.rs +assertion_line: 129 +expression: snapshot +--- + +## Input + +```css +a { + prop1: value; + prop2: value; +} + +a { + prop1: 1px; +} + +a { + prop1: a(); +} + +a { + prop1: a(1); +} + +a { + prop1: a(1,1); +} + +a { + prop1: a(1,1 1); +} + +a { + prop1: 2/3; +} + +a { + prop1: --custom; +} + + +``` + + +## AST + +``` +CssRoot { + bom_token: missing (optional), + rules: CssRuleList [ + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@0..2 "a" [] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@2..3 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@3..13 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@13..15 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssIdentifier { + value_token: IDENT@15..20 "value" [] [], + }, + ], + important: missing (optional), + }, + SEMICOLON@20..21 ";" [] [], + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@21..31 "prop2" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@31..33 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssIdentifier { + value_token: IDENT@33..38 "value" [] [], + }, + ], + important: missing (optional), + }, + SEMICOLON@38..39 ";" [] [], + ], + r_curly_token: R_CURLY@39..41 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@41..45 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@45..46 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@46..56 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@56..58 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssRegularDimension { + value: CssNumber { + value_token: CSS_NUMBER_LITERAL@58..59 "1" [] [], + }, + unit: CssIdentifier { + value_token: IDENT@59..61 "px" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@61..62 ";" [] [], + ], + r_curly_token: R_CURLY@62..64 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@64..68 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@68..69 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@69..79 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@79..81 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssAnyFunction { + css_simple_function: CssSimpleFunction { + name: CssIdentifier { + value_token: IDENT@81..82 "a" [] [], + }, + l_paren_token: L_PAREN@82..83 "(" [] [], + items: CssParameterList [], + r_paren_token: R_PAREN@83..84 ")" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@84..85 ";" [] [], + ], + r_curly_token: R_CURLY@85..87 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@87..91 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@91..92 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@92..102 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@102..104 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssAnyFunction { + css_simple_function: CssSimpleFunction { + name: CssIdentifier { + value_token: IDENT@104..105 "a" [] [], + }, + l_paren_token: L_PAREN@105..106 "(" [] [], + items: CssParameterList [ + CssParameter { + css_list_of_component_values: CssListOfComponentValues [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@106..107 "1" [] [], + }, + ], + }, + ], + r_paren_token: R_PAREN@107..108 ")" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@108..109 ";" [] [], + ], + r_curly_token: R_CURLY@109..111 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@111..115 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@115..116 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@116..126 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@126..128 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssAnyFunction { + css_simple_function: CssSimpleFunction { + name: CssIdentifier { + value_token: IDENT@128..129 "a" [] [], + }, + l_paren_token: L_PAREN@129..130 "(" [] [], + items: CssParameterList [ + CssParameter { + css_list_of_component_values: CssListOfComponentValues [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@130..131 "1" [] [], + }, + ], + }, + COMMA@131..132 "," [] [], + CssParameter { + css_list_of_component_values: CssListOfComponentValues [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@132..133 "1" [] [], + }, + ], + }, + ], + r_paren_token: R_PAREN@133..134 ")" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@134..135 ";" [] [], + ], + r_curly_token: R_CURLY@135..137 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@137..141 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@141..142 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@142..152 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@152..154 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssAnyFunction { + css_simple_function: CssSimpleFunction { + name: CssIdentifier { + value_token: IDENT@154..155 "a" [] [], + }, + l_paren_token: L_PAREN@155..156 "(" [] [], + items: CssParameterList [ + CssParameter { + css_list_of_component_values: CssListOfComponentValues [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@156..157 "1" [] [], + }, + ], + }, + COMMA@157..158 "," [] [], + CssParameter { + css_list_of_component_values: CssListOfComponentValues [ + CssNumber { + value_token: CSS_NUMBER_LITERAL@158..160 "1" [] [Whitespace(" ")], + }, + CssNumber { + value_token: CSS_NUMBER_LITERAL@160..161 "1" [] [], + }, + ], + }, + ], + r_paren_token: R_PAREN@161..162 ")" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@162..163 ";" [] [], + ], + r_curly_token: R_CURLY@163..165 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@165..169 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@169..170 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@170..180 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@180..182 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssRatio { + numerator: CssNumber { + value_token: CSS_NUMBER_LITERAL@182..183 "2" [] [], + }, + slash_token: SLASH@183..184 "/" [] [], + denominator: CssNumber { + value_token: CSS_NUMBER_LITERAL@184..185 "3" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@185..186 ";" [] [], + ], + r_curly_token: R_CURLY@186..188 "}" [Newline("\n")] [], + }, + }, + CssRule { + prelude: CssSelectorList [ + CssCompoundSelector { + nesting_selector_token: missing (optional), + simple_selector: CssTypeSelector { + namespace: missing (optional), + ident: CssIdentifier { + value_token: IDENT@188..192 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")], + }, + }, + sub_selectors: CssSubSelectorList [], + }, + ], + block: CssBlock { + l_curly_token: L_CURLY@192..193 "{" [] [], + declaration_list: CssDeclarationList [ + CssDeclaration { + name: CssIdentifier { + value_token: IDENT@193..203 "prop1" [Newline("\n"), Whitespace(" ")] [], + }, + colon_token: COLON@203..205 ":" [] [Whitespace(" ")], + value: CssListOfComponentValues [ + CssCustomProperty { + value: CssIdentifier { + value_token: IDENT@205..213 "--custom" [] [], + }, + }, + ], + important: missing (optional), + }, + SEMICOLON@213..214 ";" [] [], + ], + r_curly_token: R_CURLY@214..216 "}" [Newline("\n")] [], + }, + }, + ], + eof_token: EOF@216..218 "" [Newline("\n"), Newline("\n")] [], +} +``` + +## CST + +``` +0: CSS_ROOT@0..218 + 0: (empty) + 1: CSS_RULE_LIST@0..216 + 0: CSS_RULE@0..41 + 0: CSS_SELECTOR_LIST@0..2 + 0: CSS_COMPOUND_SELECTOR@0..2 + 0: (empty) + 1: CSS_TYPE_SELECTOR@0..2 + 0: (empty) + 1: CSS_IDENTIFIER@0..2 + 0: IDENT@0..2 "a" [] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@2..2 + 1: CSS_BLOCK@2..41 + 0: L_CURLY@2..3 "{" [] [] + 1: CSS_DECLARATION_LIST@3..39 + 0: CSS_DECLARATION@3..20 + 0: CSS_IDENTIFIER@3..13 + 0: IDENT@3..13 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@13..15 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@15..20 + 0: CSS_IDENTIFIER@15..20 + 0: IDENT@15..20 "value" [] [] + 3: (empty) + 1: SEMICOLON@20..21 ";" [] [] + 2: CSS_DECLARATION@21..38 + 0: CSS_IDENTIFIER@21..31 + 0: IDENT@21..31 "prop2" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@31..33 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@33..38 + 0: CSS_IDENTIFIER@33..38 + 0: IDENT@33..38 "value" [] [] + 3: (empty) + 3: SEMICOLON@38..39 ";" [] [] + 2: R_CURLY@39..41 "}" [Newline("\n")] [] + 1: CSS_RULE@41..64 + 0: CSS_SELECTOR_LIST@41..45 + 0: CSS_COMPOUND_SELECTOR@41..45 + 0: (empty) + 1: CSS_TYPE_SELECTOR@41..45 + 0: (empty) + 1: CSS_IDENTIFIER@41..45 + 0: IDENT@41..45 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@45..45 + 1: CSS_BLOCK@45..64 + 0: L_CURLY@45..46 "{" [] [] + 1: CSS_DECLARATION_LIST@46..62 + 0: CSS_DECLARATION@46..61 + 0: CSS_IDENTIFIER@46..56 + 0: IDENT@46..56 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@56..58 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@58..61 + 0: CSS_REGULAR_DIMENSION@58..61 + 0: CSS_NUMBER@58..59 + 0: CSS_NUMBER_LITERAL@58..59 "1" [] [] + 1: CSS_IDENTIFIER@59..61 + 0: IDENT@59..61 "px" [] [] + 3: (empty) + 1: SEMICOLON@61..62 ";" [] [] + 2: R_CURLY@62..64 "}" [Newline("\n")] [] + 2: CSS_RULE@64..87 + 0: CSS_SELECTOR_LIST@64..68 + 0: CSS_COMPOUND_SELECTOR@64..68 + 0: (empty) + 1: CSS_TYPE_SELECTOR@64..68 + 0: (empty) + 1: CSS_IDENTIFIER@64..68 + 0: IDENT@64..68 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@68..68 + 1: CSS_BLOCK@68..87 + 0: L_CURLY@68..69 "{" [] [] + 1: CSS_DECLARATION_LIST@69..85 + 0: CSS_DECLARATION@69..84 + 0: CSS_IDENTIFIER@69..79 + 0: IDENT@69..79 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@79..81 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@81..84 + 0: CSS_ANY_FUNCTION@81..84 + 0: CSS_SIMPLE_FUNCTION@81..84 + 0: CSS_IDENTIFIER@81..82 + 0: IDENT@81..82 "a" [] [] + 1: L_PAREN@82..83 "(" [] [] + 2: CSS_PARAMETER_LIST@83..83 + 3: R_PAREN@83..84 ")" [] [] + 3: (empty) + 1: SEMICOLON@84..85 ";" [] [] + 2: R_CURLY@85..87 "}" [Newline("\n")] [] + 3: CSS_RULE@87..111 + 0: CSS_SELECTOR_LIST@87..91 + 0: CSS_COMPOUND_SELECTOR@87..91 + 0: (empty) + 1: CSS_TYPE_SELECTOR@87..91 + 0: (empty) + 1: CSS_IDENTIFIER@87..91 + 0: IDENT@87..91 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@91..91 + 1: CSS_BLOCK@91..111 + 0: L_CURLY@91..92 "{" [] [] + 1: CSS_DECLARATION_LIST@92..109 + 0: CSS_DECLARATION@92..108 + 0: CSS_IDENTIFIER@92..102 + 0: IDENT@92..102 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@102..104 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@104..108 + 0: CSS_ANY_FUNCTION@104..108 + 0: CSS_SIMPLE_FUNCTION@104..108 + 0: CSS_IDENTIFIER@104..105 + 0: IDENT@104..105 "a" [] [] + 1: L_PAREN@105..106 "(" [] [] + 2: CSS_PARAMETER_LIST@106..107 + 0: CSS_PARAMETER@106..107 + 0: CSS_LIST_OF_COMPONENT_VALUES@106..107 + 0: CSS_NUMBER@106..107 + 0: CSS_NUMBER_LITERAL@106..107 "1" [] [] + 3: R_PAREN@107..108 ")" [] [] + 3: (empty) + 1: SEMICOLON@108..109 ";" [] [] + 2: R_CURLY@109..111 "}" [Newline("\n")] [] + 4: CSS_RULE@111..137 + 0: CSS_SELECTOR_LIST@111..115 + 0: CSS_COMPOUND_SELECTOR@111..115 + 0: (empty) + 1: CSS_TYPE_SELECTOR@111..115 + 0: (empty) + 1: CSS_IDENTIFIER@111..115 + 0: IDENT@111..115 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@115..115 + 1: CSS_BLOCK@115..137 + 0: L_CURLY@115..116 "{" [] [] + 1: CSS_DECLARATION_LIST@116..135 + 0: CSS_DECLARATION@116..134 + 0: CSS_IDENTIFIER@116..126 + 0: IDENT@116..126 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@126..128 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@128..134 + 0: CSS_ANY_FUNCTION@128..134 + 0: CSS_SIMPLE_FUNCTION@128..134 + 0: CSS_IDENTIFIER@128..129 + 0: IDENT@128..129 "a" [] [] + 1: L_PAREN@129..130 "(" [] [] + 2: CSS_PARAMETER_LIST@130..133 + 0: CSS_PARAMETER@130..131 + 0: CSS_LIST_OF_COMPONENT_VALUES@130..131 + 0: CSS_NUMBER@130..131 + 0: CSS_NUMBER_LITERAL@130..131 "1" [] [] + 1: COMMA@131..132 "," [] [] + 2: CSS_PARAMETER@132..133 + 0: CSS_LIST_OF_COMPONENT_VALUES@132..133 + 0: CSS_NUMBER@132..133 + 0: CSS_NUMBER_LITERAL@132..133 "1" [] [] + 3: R_PAREN@133..134 ")" [] [] + 3: (empty) + 1: SEMICOLON@134..135 ";" [] [] + 2: R_CURLY@135..137 "}" [Newline("\n")] [] + 5: CSS_RULE@137..165 + 0: CSS_SELECTOR_LIST@137..141 + 0: CSS_COMPOUND_SELECTOR@137..141 + 0: (empty) + 1: CSS_TYPE_SELECTOR@137..141 + 0: (empty) + 1: CSS_IDENTIFIER@137..141 + 0: IDENT@137..141 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@141..141 + 1: CSS_BLOCK@141..165 + 0: L_CURLY@141..142 "{" [] [] + 1: CSS_DECLARATION_LIST@142..163 + 0: CSS_DECLARATION@142..162 + 0: CSS_IDENTIFIER@142..152 + 0: IDENT@142..152 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@152..154 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@154..162 + 0: CSS_ANY_FUNCTION@154..162 + 0: CSS_SIMPLE_FUNCTION@154..162 + 0: CSS_IDENTIFIER@154..155 + 0: IDENT@154..155 "a" [] [] + 1: L_PAREN@155..156 "(" [] [] + 2: CSS_PARAMETER_LIST@156..161 + 0: CSS_PARAMETER@156..157 + 0: CSS_LIST_OF_COMPONENT_VALUES@156..157 + 0: CSS_NUMBER@156..157 + 0: CSS_NUMBER_LITERAL@156..157 "1" [] [] + 1: COMMA@157..158 "," [] [] + 2: CSS_PARAMETER@158..161 + 0: CSS_LIST_OF_COMPONENT_VALUES@158..161 + 0: CSS_NUMBER@158..160 + 0: CSS_NUMBER_LITERAL@158..160 "1" [] [Whitespace(" ")] + 1: CSS_NUMBER@160..161 + 0: CSS_NUMBER_LITERAL@160..161 "1" [] [] + 3: R_PAREN@161..162 ")" [] [] + 3: (empty) + 1: SEMICOLON@162..163 ";" [] [] + 2: R_CURLY@163..165 "}" [Newline("\n")] [] + 6: CSS_RULE@165..188 + 0: CSS_SELECTOR_LIST@165..169 + 0: CSS_COMPOUND_SELECTOR@165..169 + 0: (empty) + 1: CSS_TYPE_SELECTOR@165..169 + 0: (empty) + 1: CSS_IDENTIFIER@165..169 + 0: IDENT@165..169 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@169..169 + 1: CSS_BLOCK@169..188 + 0: L_CURLY@169..170 "{" [] [] + 1: CSS_DECLARATION_LIST@170..186 + 0: CSS_DECLARATION@170..185 + 0: CSS_IDENTIFIER@170..180 + 0: IDENT@170..180 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@180..182 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@182..185 + 0: CSS_RATIO@182..185 + 0: CSS_NUMBER@182..183 + 0: CSS_NUMBER_LITERAL@182..183 "2" [] [] + 1: SLASH@183..184 "/" [] [] + 2: CSS_NUMBER@184..185 + 0: CSS_NUMBER_LITERAL@184..185 "3" [] [] + 3: (empty) + 1: SEMICOLON@185..186 ";" [] [] + 2: R_CURLY@186..188 "}" [Newline("\n")] [] + 7: CSS_RULE@188..216 + 0: CSS_SELECTOR_LIST@188..192 + 0: CSS_COMPOUND_SELECTOR@188..192 + 0: (empty) + 1: CSS_TYPE_SELECTOR@188..192 + 0: (empty) + 1: CSS_IDENTIFIER@188..192 + 0: IDENT@188..192 "a" [Newline("\n"), Newline("\n")] [Whitespace(" ")] + 2: CSS_SUB_SELECTOR_LIST@192..192 + 1: CSS_BLOCK@192..216 + 0: L_CURLY@192..193 "{" [] [] + 1: CSS_DECLARATION_LIST@193..214 + 0: CSS_DECLARATION@193..213 + 0: CSS_IDENTIFIER@193..203 + 0: IDENT@193..203 "prop1" [Newline("\n"), Whitespace(" ")] [] + 1: COLON@203..205 ":" [] [Whitespace(" ")] + 2: CSS_LIST_OF_COMPONENT_VALUES@205..213 + 0: CSS_CUSTOM_PROPERTY@205..213 + 0: CSS_IDENTIFIER@205..213 + 0: IDENT@205..213 "--custom" [] [] + 3: (empty) + 1: SEMICOLON@213..214 ";" [] [] + 2: R_CURLY@214..216 "}" [Newline("\n")] [] + 2: EOF@216..218 "" [Newline("\n"), Newline("\n")] [] + +``` + + diff --git a/crates/biome_css_parser/tests/spec_test.rs b/crates/biome_css_parser/tests/spec_test.rs index b5da217c25d3..608e1fb31bc1 100644 --- a/crates/biome_css_parser/tests/spec_test.rs +++ b/crates/biome_css_parser/tests/spec_test.rs @@ -134,8 +134,43 @@ pub fn run(test_case: &str, _snapshot_name: &str, test_directory: &str, outcome_ #[test] pub fn quick_test() { let code = r#" - :host(.footer) {} - + a { + prop1: value; + prop2: value; + } + + a { + prop1: 1px; + } + + a { + prop1: a(); + } + + a { + prop1: a(1); + } + + a { + prop1: a(1,1); + } + + a { + prop1: a(1,1 1); + } + + a { + prop1: 2/3; + } + + a { + prop1: --custom; + } + + + + + "#; let root = parse_css( code, @@ -143,7 +178,6 @@ pub fn quick_test() { ); let syntax = root.syntax(); dbg!(&syntax, root.diagnostics(), root.has_errors()); - if has_bogus_nodes_or_empty_slots(&syntax) { panic!( "modified tree has bogus nodes or empty slots:\n{syntax:#?} \n\n {}", diff --git a/crates/biome_css_syntax/src/generated/kind.rs b/crates/biome_css_syntax/src/generated/kind.rs index 86db7ee8e3ef..7bb13732819b 100644 --- a/crates/biome_css_syntax/src/generated/kind.rs +++ b/crates/biome_css_syntax/src/generated/kind.rs @@ -256,7 +256,6 @@ pub enum CssSyntaxKind { CSS_ANY_FUNCTION, CSS_BLOCK, CSS_DECLARATION, - CSS_DIMENSION, CSS_IDENTIFIER, CSS_KEYFRAMES_BLOCK, CSS_KEYFRAMES_SELECTOR, @@ -270,9 +269,13 @@ pub enum CssSyntaxKind { CSS_VAR_FUNCTION_VALUE, CSS_ATTRIBUTE_LIST, CSS_DECLARATION_LIST, + CSS_LIST_OF_COMPONENT_VALUES, CSS_KEYFRAMES_SELECTOR_LIST, CSS_PARAMETER_LIST, CSS_DECLARATION_IMPORTANT, + CSS_UNIT, + CSS_PERCENT_DIMENSION, + CSS_REGULAR_DIMENSION, CSS_NAMESPACE, CSS_NAMED_NAMESPACE_PREFIX, CSS_UNIVERSAL_NAMESPACE_PREFIX, @@ -339,6 +342,9 @@ pub enum CssSyntaxKind { CSS_BOGUS_PSEUDO_CLASS, CSS_BOGUS_PSEUDO_ELEMENT, CSS_BOGUS_AT_RULE, + CSS_BOGUS_DECLARATION_ITEM, + CSS_BOGUS_COMPONENT_VALUE, + CSS_BOGUS_PARAMETER, #[doc(hidden)] __LAST, } diff --git a/crates/biome_css_syntax/src/generated/macros.rs b/crates/biome_css_syntax/src/generated/macros.rs index e82b0906fa7e..3d27b9423926 100644 --- a/crates/biome_css_syntax/src/generated/macros.rs +++ b/crates/biome_css_syntax/src/generated/macros.rs @@ -80,10 +80,6 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssDeclarationImportant::new_unchecked(node) }; $body } - $crate::CssSyntaxKind::CSS_DIMENSION => { - let $pattern = unsafe { $crate::CssDimension::new_unchecked(node) }; - $body - } $crate::CssSyntaxKind::CSS_ID_SELECTOR => { let $pattern = unsafe { $crate::CssIdSelector::new_unchecked(node) }; $body @@ -168,6 +164,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssParameter::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::CSS_PERCENT_DIMENSION => { + let $pattern = unsafe { $crate::CssPercentDimension::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::CSS_PERCENTAGE => { let $pattern = unsafe { $crate::CssPercentage::new_unchecked(node) }; $body @@ -269,6 +269,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssRatio::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::CSS_REGULAR_DIMENSION => { + let $pattern = unsafe { $crate::CssRegularDimension::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::CSS_RELATIVE_SELECTOR => { let $pattern = unsafe { $crate::CssRelativeSelector::new_unchecked(node) }; $body @@ -358,6 +362,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::CssKeyframesSelectorList::new_unchecked(node) }; $body } + $crate::CssSyntaxKind::CSS_LIST_OF_COMPONENT_VALUES => { + let $pattern = unsafe { $crate::CssListOfComponentValues::new_unchecked(node) }; + $body + } $crate::CssSyntaxKind::CSS_MEDIA_QUERY_LIST => { let $pattern = unsafe { $crate::CssMediaQueryList::new_unchecked(node) }; $body diff --git a/crates/biome_css_syntax/src/generated/nodes.rs b/crates/biome_css_syntax/src/generated/nodes.rs index f247a8585b3c..79a7a1a369c3 100644 --- a/crates/biome_css_syntax/src/generated/nodes.rs +++ b/crates/biome_css_syntax/src/generated/nodes.rs @@ -601,11 +601,11 @@ impl CssCustomProperty { } pub fn as_fields(&self) -> CssCustomPropertyFields { CssCustomPropertyFields { - value_token: self.value_token(), + value: self.value(), } } - pub fn value_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, 0usize) + pub fn value(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) } } #[cfg(feature = "serde")] @@ -619,7 +619,7 @@ impl Serialize for CssCustomProperty { } #[cfg_attr(feature = "serde", derive(Serialize))] pub struct CssCustomPropertyFields { - pub value_token: SyntaxResult, + pub value: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] pub struct CssDeclaration { @@ -638,26 +638,22 @@ impl CssDeclaration { pub fn as_fields(&self) -> CssDeclarationFields { CssDeclarationFields { name: self.name(), - css_custom_property: self.css_custom_property(), colon_token: self.colon_token(), value: self.value(), important: self.important(), } } - pub fn name(&self) -> SyntaxResult { + pub fn name(&self) -> SyntaxResult { support::required_node(&self.syntax, 0usize) } - pub fn css_custom_property(&self) -> SyntaxResult { - support::required_node(&self.syntax, 1usize) - } pub fn colon_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, 2usize) + support::required_token(&self.syntax, 1usize) } - pub fn value(&self) -> SyntaxResult { - support::required_node(&self.syntax, 3usize) + pub fn value(&self) -> CssListOfComponentValues { + support::list(&self.syntax, 2usize) } pub fn important(&self) -> Option { - support::node(&self.syntax, 4usize) + support::node(&self.syntax, 3usize) } } #[cfg(feature = "serde")] @@ -671,10 +667,9 @@ impl Serialize for CssDeclaration { } #[cfg_attr(feature = "serde", derive(Serialize))] pub struct CssDeclarationFields { - pub name: SyntaxResult, - pub css_custom_property: SyntaxResult, + pub name: SyntaxResult, pub colon_token: SyntaxResult, - pub value: SyntaxResult, + pub value: CssListOfComponentValues, pub important: Option, } #[derive(Clone, PartialEq, Eq, Hash)] @@ -719,47 +714,6 @@ pub struct CssDeclarationImportantFields { pub important_token: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] -pub struct CssDimension { - pub(crate) syntax: SyntaxNode, -} -impl CssDimension { - #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] - #[doc = r""] - #[doc = r" # Safety"] - #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] - #[doc = r" or a match on [SyntaxNode::kind]"] - #[inline] - pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { - Self { syntax } - } - pub fn as_fields(&self) -> CssDimensionFields { - CssDimensionFields { - value: self.value(), - unit: self.unit(), - } - } - pub fn value(&self) -> SyntaxResult { - support::required_node(&self.syntax, 0usize) - } - pub fn unit(&self) -> SyntaxResult { - support::required_node(&self.syntax, 1usize) - } -} -#[cfg(feature = "serde")] -impl Serialize for CssDimension { - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - self.as_fields().serialize(serializer) - } -} -#[cfg_attr(feature = "serde", derive(Serialize))] -pub struct CssDimensionFields { - pub value: SyntaxResult, - pub unit: SyntaxResult, -} -#[derive(Clone, PartialEq, Eq, Hash)] pub struct CssIdSelector { pub(crate) syntax: SyntaxNode, } @@ -1637,11 +1591,11 @@ impl CssParameter { } pub fn as_fields(&self) -> CssParameterFields { CssParameterFields { - any_css_value: self.any_css_value(), + css_list_of_component_values: self.css_list_of_component_values(), } } - pub fn any_css_value(&self) -> SyntaxResult { - support::required_node(&self.syntax, 0usize) + pub fn css_list_of_component_values(&self) -> CssListOfComponentValues { + support::list(&self.syntax, 0usize) } } #[cfg(feature = "serde")] @@ -1655,7 +1609,48 @@ impl Serialize for CssParameter { } #[cfg_attr(feature = "serde", derive(Serialize))] pub struct CssParameterFields { - pub any_css_value: SyntaxResult, + pub css_list_of_component_values: CssListOfComponentValues, +} +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssPercentDimension { + pub(crate) syntax: SyntaxNode, +} +impl CssPercentDimension { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> CssPercentDimensionFields { + CssPercentDimensionFields { + value: self.value(), + unit_token: self.unit_token(), + } + } + pub fn value(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn unit_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } +} +#[cfg(feature = "serde")] +impl Serialize for CssPercentDimension { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct CssPercentDimensionFields { + pub value: SyntaxResult, + pub unit_token: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] pub struct CssPercentage { @@ -2585,14 +2580,18 @@ impl CssRatio { pub fn as_fields(&self) -> CssRatioFields { CssRatioFields { numerator: self.numerator(), + slash_token: self.slash_token(), denominator: self.denominator(), } } pub fn numerator(&self) -> SyntaxResult { support::required_node(&self.syntax, 0usize) } + pub fn slash_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, 1usize) + } pub fn denominator(&self) -> SyntaxResult { - support::required_node(&self.syntax, 1usize) + support::required_node(&self.syntax, 2usize) } } #[cfg(feature = "serde")] @@ -2607,9 +2606,51 @@ impl Serialize for CssRatio { #[cfg_attr(feature = "serde", derive(Serialize))] pub struct CssRatioFields { pub numerator: SyntaxResult, + pub slash_token: SyntaxResult, pub denominator: SyntaxResult, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct CssRegularDimension { + pub(crate) syntax: SyntaxNode, +} +impl CssRegularDimension { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> CssRegularDimensionFields { + CssRegularDimensionFields { + value: self.value(), + unit: self.unit(), + } + } + pub fn value(&self) -> SyntaxResult { + support::required_node(&self.syntax, 0usize) + } + pub fn unit(&self) -> SyntaxResult { + support::required_node(&self.syntax, 1usize) + } +} +#[cfg(feature = "serde")] +impl Serialize for CssRegularDimension { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct CssRegularDimensionFields { + pub value: SyntaxResult, + pub unit: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct CssRelativeSelector { pub(crate) syntax: SyntaxNode, } @@ -3129,6 +3170,46 @@ impl AnyCssCompoundSelector { } #[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] +pub enum AnyCssDeclarationName { + CssCustomProperty(CssCustomProperty), + CssIdentifier(CssIdentifier), +} +impl AnyCssDeclarationName { + pub fn as_css_custom_property(&self) -> Option<&CssCustomProperty> { + match &self { + AnyCssDeclarationName::CssCustomProperty(item) => Some(item), + _ => None, + } + } + pub fn as_css_identifier(&self) -> Option<&CssIdentifier> { + match &self { + AnyCssDeclarationName::CssIdentifier(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize))] +pub enum AnyCssDimension { + CssPercentage(CssPercentage), + CssRegularDimension(CssRegularDimension), +} +impl AnyCssDimension { + pub fn as_css_percentage(&self) -> Option<&CssPercentage> { + match &self { + AnyCssDimension::CssPercentage(item) => Some(item), + _ => None, + } + } + pub fn as_css_regular_dimension(&self) -> Option<&CssRegularDimension> { + match &self { + AnyCssDimension::CssRegularDimension(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize))] pub enum AnyCssMediaQueryFeatureType { CssMediaQueryFeatureBoolean(CssMediaQueryFeatureBoolean), CssMediaQueryFeatureCompare(CssMediaQueryFeatureCompare), @@ -3539,30 +3620,30 @@ impl AnyCssSubSelector { #[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] pub enum AnyCssValue { + AnyCssDimension(AnyCssDimension), CssAnyFunction(CssAnyFunction), CssCustomProperty(CssCustomProperty), - CssDimension(CssDimension), CssIdentifier(CssIdentifier), CssNumber(CssNumber), CssRatio(CssRatio), CssString(CssString), } impl AnyCssValue { - pub fn as_css_any_function(&self) -> Option<&CssAnyFunction> { + pub fn as_any_css_dimension(&self) -> Option<&AnyCssDimension> { match &self { - AnyCssValue::CssAnyFunction(item) => Some(item), + AnyCssValue::AnyCssDimension(item) => Some(item), _ => None, } } - pub fn as_css_custom_property(&self) -> Option<&CssCustomProperty> { + pub fn as_css_any_function(&self) -> Option<&CssAnyFunction> { match &self { - AnyCssValue::CssCustomProperty(item) => Some(item), + AnyCssValue::CssAnyFunction(item) => Some(item), _ => None, } } - pub fn as_css_dimension(&self) -> Option<&CssDimension> { + pub fn as_css_custom_property(&self) -> Option<&CssCustomProperty> { match &self { - AnyCssValue::CssDimension(item) => Some(item), + AnyCssValue::CssCustomProperty(item) => Some(item), _ => None, } } @@ -4165,10 +4246,7 @@ impl AstNode for CssCustomProperty { impl std::fmt::Debug for CssCustomProperty { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CssCustomProperty") - .field( - "value_token", - &support::DebugSyntaxResult(self.value_token()), - ) + .field("value", &support::DebugSyntaxResult(self.value())) .finish() } } @@ -4207,15 +4285,11 @@ impl std::fmt::Debug for CssDeclaration { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CssDeclaration") .field("name", &support::DebugSyntaxResult(self.name())) - .field( - "css_custom_property", - &support::DebugSyntaxResult(self.css_custom_property()), - ) .field( "colon_token", &support::DebugSyntaxResult(self.colon_token()), ) - .field("value", &support::DebugSyntaxResult(self.value())) + .field("value", &self.value()) .field( "important", &support::DebugOptionalElement(self.important()), @@ -4275,45 +4349,6 @@ impl From for SyntaxElement { n.syntax.into() } } -impl AstNode for CssDimension { - type Language = Language; - const KIND_SET: SyntaxKindSet = - SyntaxKindSet::from_raw(RawSyntaxKind(CSS_DIMENSION as u16)); - fn can_cast(kind: SyntaxKind) -> bool { - kind == CSS_DIMENSION - } - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - fn syntax(&self) -> &SyntaxNode { - &self.syntax - } - fn into_syntax(self) -> SyntaxNode { - self.syntax - } -} -impl std::fmt::Debug for CssDimension { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CssDimension") - .field("value", &support::DebugSyntaxResult(self.value())) - .field("unit", &support::DebugSyntaxResult(self.unit())) - .finish() - } -} -impl From for SyntaxNode { - fn from(n: CssDimension) -> SyntaxNode { - n.syntax - } -} -impl From for SyntaxElement { - fn from(n: CssDimension) -> SyntaxElement { - n.syntax.into() - } -} impl AstNode for CssIdSelector { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -5181,8 +5216,8 @@ impl std::fmt::Debug for CssParameter { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CssParameter") .field( - "any_css_value", - &support::DebugSyntaxResult(self.any_css_value()), + "css_list_of_component_values", + &self.css_list_of_component_values(), ) .finish() } @@ -5197,6 +5232,45 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for CssPercentDimension { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_PERCENT_DIMENSION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_PERCENT_DIMENSION + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for CssPercentDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CssPercentDimension") + .field("value", &support::DebugSyntaxResult(self.value())) + .field("unit_token", &support::DebugSyntaxResult(self.unit_token())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: CssPercentDimension) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssPercentDimension) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for CssPercentage { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -6100,6 +6174,10 @@ impl std::fmt::Debug for CssRatio { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("CssRatio") .field("numerator", &support::DebugSyntaxResult(self.numerator())) + .field( + "slash_token", + &support::DebugSyntaxResult(self.slash_token()), + ) .field( "denominator", &support::DebugSyntaxResult(self.denominator()), @@ -6117,6 +6195,45 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for CssRegularDimension { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_REGULAR_DIMENSION as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_REGULAR_DIMENSION + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for CssRegularDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CssRegularDimension") + .field("value", &support::DebugSyntaxResult(self.value())) + .field("unit", &support::DebugSyntaxResult(self.unit())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: CssRegularDimension) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: CssRegularDimension) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for CssRelativeSelector { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -6782,6 +6899,130 @@ impl From for SyntaxElement { node.into() } } +impl From for AnyCssDeclarationName { + fn from(node: CssCustomProperty) -> AnyCssDeclarationName { + AnyCssDeclarationName::CssCustomProperty(node) + } +} +impl From for AnyCssDeclarationName { + fn from(node: CssIdentifier) -> AnyCssDeclarationName { + AnyCssDeclarationName::CssIdentifier(node) + } +} +impl AstNode for AnyCssDeclarationName { + type Language = Language; + const KIND_SET: SyntaxKindSet = + CssCustomProperty::KIND_SET.union(CssIdentifier::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, CSS_CUSTOM_PROPERTY | CSS_IDENTIFIER) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_CUSTOM_PROPERTY => { + AnyCssDeclarationName::CssCustomProperty(CssCustomProperty { syntax }) + } + CSS_IDENTIFIER => AnyCssDeclarationName::CssIdentifier(CssIdentifier { syntax }), + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyCssDeclarationName::CssCustomProperty(it) => &it.syntax, + AnyCssDeclarationName::CssIdentifier(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyCssDeclarationName::CssCustomProperty(it) => it.syntax, + AnyCssDeclarationName::CssIdentifier(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyCssDeclarationName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyCssDeclarationName::CssCustomProperty(it) => std::fmt::Debug::fmt(it, f), + AnyCssDeclarationName::CssIdentifier(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssDeclarationName) -> SyntaxNode { + match n { + AnyCssDeclarationName::CssCustomProperty(it) => it.into(), + AnyCssDeclarationName::CssIdentifier(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssDeclarationName) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} +impl From for AnyCssDimension { + fn from(node: CssPercentage) -> AnyCssDimension { + AnyCssDimension::CssPercentage(node) + } +} +impl From for AnyCssDimension { + fn from(node: CssRegularDimension) -> AnyCssDimension { + AnyCssDimension::CssRegularDimension(node) + } +} +impl AstNode for AnyCssDimension { + type Language = Language; + const KIND_SET: SyntaxKindSet = + CssPercentage::KIND_SET.union(CssRegularDimension::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, CSS_PERCENTAGE | CSS_REGULAR_DIMENSION) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + CSS_PERCENTAGE => AnyCssDimension::CssPercentage(CssPercentage { syntax }), + CSS_REGULAR_DIMENSION => { + AnyCssDimension::CssRegularDimension(CssRegularDimension { syntax }) + } + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyCssDimension::CssPercentage(it) => &it.syntax, + AnyCssDimension::CssRegularDimension(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyCssDimension::CssPercentage(it) => it.syntax, + AnyCssDimension::CssRegularDimension(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyCssDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyCssDimension::CssPercentage(it) => std::fmt::Debug::fmt(it, f), + AnyCssDimension::CssRegularDimension(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyCssDimension) -> SyntaxNode { + match n { + AnyCssDimension::CssPercentage(it) => it.into(), + AnyCssDimension::CssRegularDimension(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyCssDimension) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} impl From for AnyCssMediaQueryFeatureType { fn from(node: CssMediaQueryFeatureBoolean) -> AnyCssMediaQueryFeatureType { AnyCssMediaQueryFeatureType::CssMediaQueryFeatureBoolean(node) @@ -7947,11 +8188,6 @@ impl From for AnyCssValue { AnyCssValue::CssCustomProperty(node) } } -impl From for AnyCssValue { - fn from(node: CssDimension) -> AnyCssValue { - AnyCssValue::CssDimension(node) - } -} impl From for AnyCssValue { fn from(node: CssIdentifier) -> AnyCssValue { AnyCssValue::CssIdentifier(node) @@ -7974,35 +8210,35 @@ impl From for AnyCssValue { } impl AstNode for AnyCssValue { type Language = Language; - const KIND_SET: SyntaxKindSet = CssAnyFunction::KIND_SET + const KIND_SET: SyntaxKindSet = AnyCssDimension::KIND_SET + .union(CssAnyFunction::KIND_SET) .union(CssCustomProperty::KIND_SET) - .union(CssDimension::KIND_SET) .union(CssIdentifier::KIND_SET) .union(CssNumber::KIND_SET) .union(CssRatio::KIND_SET) .union(CssString::KIND_SET); fn can_cast(kind: SyntaxKind) -> bool { - matches!( - kind, - CSS_ANY_FUNCTION - | CSS_CUSTOM_PROPERTY - | CSS_DIMENSION - | CSS_IDENTIFIER - | CSS_NUMBER - | CSS_RATIO - | CSS_STRING - ) + match kind { + CSS_ANY_FUNCTION | CSS_CUSTOM_PROPERTY | CSS_IDENTIFIER | CSS_NUMBER | CSS_RATIO + | CSS_STRING => true, + k if AnyCssDimension::can_cast(k) => true, + _ => false, + } } fn cast(syntax: SyntaxNode) -> Option { let res = match syntax.kind() { CSS_ANY_FUNCTION => AnyCssValue::CssAnyFunction(CssAnyFunction { syntax }), CSS_CUSTOM_PROPERTY => AnyCssValue::CssCustomProperty(CssCustomProperty { syntax }), - CSS_DIMENSION => AnyCssValue::CssDimension(CssDimension { syntax }), CSS_IDENTIFIER => AnyCssValue::CssIdentifier(CssIdentifier { syntax }), CSS_NUMBER => AnyCssValue::CssNumber(CssNumber { syntax }), CSS_RATIO => AnyCssValue::CssRatio(CssRatio { syntax }), CSS_STRING => AnyCssValue::CssString(CssString { syntax }), - _ => return None, + _ => { + if let Some(any_css_dimension) = AnyCssDimension::cast(syntax) { + return Some(AnyCssValue::AnyCssDimension(any_css_dimension)); + } + return None; + } }; Some(res) } @@ -8010,31 +8246,31 @@ impl AstNode for AnyCssValue { match self { AnyCssValue::CssAnyFunction(it) => &it.syntax, AnyCssValue::CssCustomProperty(it) => &it.syntax, - AnyCssValue::CssDimension(it) => &it.syntax, AnyCssValue::CssIdentifier(it) => &it.syntax, AnyCssValue::CssNumber(it) => &it.syntax, AnyCssValue::CssRatio(it) => &it.syntax, AnyCssValue::CssString(it) => &it.syntax, + AnyCssValue::AnyCssDimension(it) => it.syntax(), } } fn into_syntax(self) -> SyntaxNode { match self { AnyCssValue::CssAnyFunction(it) => it.syntax, AnyCssValue::CssCustomProperty(it) => it.syntax, - AnyCssValue::CssDimension(it) => it.syntax, AnyCssValue::CssIdentifier(it) => it.syntax, AnyCssValue::CssNumber(it) => it.syntax, AnyCssValue::CssRatio(it) => it.syntax, AnyCssValue::CssString(it) => it.syntax, + AnyCssValue::AnyCssDimension(it) => it.into_syntax(), } } } impl std::fmt::Debug for AnyCssValue { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { + AnyCssValue::AnyCssDimension(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssAnyFunction(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssCustomProperty(it) => std::fmt::Debug::fmt(it, f), - AnyCssValue::CssDimension(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssIdentifier(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssNumber(it) => std::fmt::Debug::fmt(it, f), AnyCssValue::CssRatio(it) => std::fmt::Debug::fmt(it, f), @@ -8045,9 +8281,9 @@ impl std::fmt::Debug for AnyCssValue { impl From for SyntaxNode { fn from(n: AnyCssValue) -> SyntaxNode { match n { + AnyCssValue::AnyCssDimension(it) => it.into(), AnyCssValue::CssAnyFunction(it) => it.into(), AnyCssValue::CssCustomProperty(it) => it.into(), - AnyCssValue::CssDimension(it) => it.into(), AnyCssValue::CssIdentifier(it) => it.into(), AnyCssValue::CssNumber(it) => it.into(), AnyCssValue::CssRatio(it) => it.into(), @@ -8076,6 +8312,16 @@ impl std::fmt::Display for AnyCssCompoundSelector { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AnyCssDeclarationName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for AnyCssDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AnyCssMediaQueryFeatureType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -8226,11 +8472,6 @@ impl std::fmt::Display for CssDeclarationImportant { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for CssDimension { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Display::fmt(self.syntax(), f) - } -} impl std::fmt::Display for CssIdSelector { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -8331,6 +8572,11 @@ impl std::fmt::Display for CssParameter { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CssPercentDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for CssPercentage { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -8436,6 +8682,11 @@ impl std::fmt::Display for CssRatio { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for CssRegularDimension { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for CssRelativeSelector { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -9078,7 +9329,7 @@ impl Serialize for CssDeclarationList { seq.end() } } -impl AstNodeList for CssDeclarationList { +impl AstSeparatedList for CssDeclarationList { type Language = Language; type Node = CssDeclaration; fn syntax_list(&self) -> &SyntaxList { @@ -9091,19 +9342,19 @@ impl AstNodeList for CssDeclarationList { impl Debug for CssDeclarationList { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("CssDeclarationList ")?; - f.debug_list().entries(self.iter()).finish() + f.debug_list().entries(self.elements()).finish() } } -impl IntoIterator for &CssDeclarationList { - type Item = CssDeclaration; - type IntoIter = AstNodeListIterator; +impl IntoIterator for CssDeclarationList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl IntoIterator for CssDeclarationList { - type Item = CssDeclaration; - type IntoIter = AstNodeListIterator; +impl IntoIterator for &CssDeclarationList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } @@ -9275,6 +9526,89 @@ impl IntoIterator for &CssKeyframesSelectorList { } } #[derive(Clone, Eq, PartialEq, Hash)] +pub struct CssListOfComponentValues { + syntax_list: SyntaxList, +} +impl CssListOfComponentValues { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { + syntax_list: syntax.into_list(), + } + } +} +impl AstNode for CssListOfComponentValues { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(CSS_LIST_OF_COMPONENT_VALUES as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == CSS_LIST_OF_COMPONENT_VALUES + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(CssListOfComponentValues { + syntax_list: syntax.into_list(), + }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + self.syntax_list.node() + } + fn into_syntax(self) -> SyntaxNode { + self.syntax_list.into_node() + } +} +#[cfg(feature = "serde")] +impl Serialize for CssListOfComponentValues { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_seq(Some(self.len()))?; + for e in self.iter() { + seq.serialize_element(&e)?; + } + seq.end() + } +} +impl AstNodeList for CssListOfComponentValues { + type Language = Language; + type Node = AnyCssValue; + fn syntax_list(&self) -> &SyntaxList { + &self.syntax_list + } + fn into_syntax_list(self) -> SyntaxList { + self.syntax_list + } +} +impl Debug for CssListOfComponentValues { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str("CssListOfComponentValues ")?; + f.debug_list().entries(self.iter()).finish() + } +} +impl IntoIterator for &CssListOfComponentValues { + type Item = AnyCssValue; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +impl IntoIterator for CssListOfComponentValues { + type Item = AnyCssValue; + type IntoIter = AstNodeListIterator; + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} +#[derive(Clone, Eq, PartialEq, Hash)] pub struct CssMediaQueryList { syntax_list: SyntaxList, } @@ -9410,7 +9744,7 @@ impl Serialize for CssParameterList { seq.end() } } -impl AstNodeList for CssParameterList { +impl AstSeparatedList for CssParameterList { type Language = Language; type Node = CssParameter; fn syntax_list(&self) -> &SyntaxList { @@ -9423,19 +9757,19 @@ impl AstNodeList for CssParameterList { impl Debug for CssParameterList { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.write_str("CssParameterList ")?; - f.debug_list().entries(self.iter()).finish() + f.debug_list().entries(self.elements()).finish() } } -impl IntoIterator for &CssParameterList { - type Item = CssParameter; - type IntoIter = AstNodeListIterator; +impl IntoIterator for CssParameterList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } } -impl IntoIterator for CssParameterList { - type Item = CssParameter; - type IntoIter = AstNodeListIterator; +impl IntoIterator for &CssParameterList { + type Item = SyntaxResult; + type IntoIter = AstSeparatedListNodesIterator; fn into_iter(self) -> Self::IntoIter { self.iter() } diff --git a/crates/biome_css_syntax/src/generated/nodes_mut.rs b/crates/biome_css_syntax/src/generated/nodes_mut.rs index 3624d7be782d..054b795ba017 100644 --- a/crates/biome_css_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_css_syntax/src/generated/nodes_mut.rs @@ -228,41 +228,35 @@ impl CssCounterStyleAtRule { } } impl CssCustomProperty { - pub fn with_value_token(self, element: SyntaxToken) -> Self { + pub fn with_value(self, element: CssIdentifier) -> Self { Self::unwrap_cast( self.syntax - .splice_slots(0usize..=0usize, once(Some(element.into()))), + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), ) } } impl CssDeclaration { - pub fn with_name(self, element: CssIdentifier) -> Self { + pub fn with_name(self, element: AnyCssDeclarationName) -> Self { Self::unwrap_cast( self.syntax .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), ) } - pub fn with_css_custom_property(self, element: CssCustomProperty) -> Self { - Self::unwrap_cast( - self.syntax - .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), - ) - } pub fn with_colon_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( self.syntax - .splice_slots(2usize..=2usize, once(Some(element.into()))), + .splice_slots(1usize..=1usize, once(Some(element.into()))), ) } - pub fn with_value(self, element: AnyCssValue) -> Self { + pub fn with_value(self, element: CssListOfComponentValues) -> Self { Self::unwrap_cast( self.syntax - .splice_slots(3usize..=3usize, once(Some(element.into_syntax().into()))), + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), ) } pub fn with_important(self, element: Option) -> Self { Self::unwrap_cast(self.syntax.splice_slots( - 4usize..=4usize, + 3usize..=3usize, once(element.map(|element| element.into_syntax().into())), )) } @@ -281,20 +275,6 @@ impl CssDeclarationImportant { ) } } -impl CssDimension { - pub fn with_value(self, element: CssNumber) -> Self { - Self::unwrap_cast( - self.syntax - .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), - ) - } - pub fn with_unit(self, element: CssIdentifier) -> Self { - Self::unwrap_cast( - self.syntax - .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), - ) - } -} impl CssIdSelector { pub fn with_hash_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( @@ -664,13 +644,27 @@ impl CssNumber { } } impl CssParameter { - pub fn with_any_css_value(self, element: AnyCssValue) -> Self { + pub fn with_css_list_of_component_values(self, element: CssListOfComponentValues) -> Self { Self::unwrap_cast( self.syntax .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), ) } } +impl CssPercentDimension { + pub fn with_value(self, element: CssNumber) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_unit_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } +} impl CssPercentage { pub fn with_value(self, element: CssNumber) -> Self { Self::unwrap_cast( @@ -1072,7 +1066,27 @@ impl CssRatio { .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), ) } + pub fn with_slash_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(1usize..=1usize, once(Some(element.into()))), + ) + } pub fn with_denominator(self, element: CssNumber) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(2usize..=2usize, once(Some(element.into_syntax().into()))), + ) + } +} +impl CssRegularDimension { + pub fn with_value(self, element: CssNumber) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), + ) + } + pub fn with_unit(self, element: CssIdentifier) -> Self { Self::unwrap_cast( self.syntax .splice_slots(1usize..=1usize, once(Some(element.into_syntax().into()))), diff --git a/crates/biome_js_unicode_table/src/tables.rs b/crates/biome_js_unicode_table/src/tables.rs index 3ed00b032140..7700038e9f9c 100644 --- a/crates/biome_js_unicode_table/src/tables.rs +++ b/crates/biome_js_unicode_table/src/tables.rs @@ -318,6 +318,7 @@ pub mod derived_property { ('ῠ', 'Ῥ'), ('ῲ', 'ῴ'), ('ῶ', 'ῼ'), + ('\u{200c}', '\u{200d}'), ('‿', '⁀'), ('⁔', '⁔'), ('ⁱ', 'ⁱ'), @@ -362,8 +363,7 @@ pub mod derived_property { ('〸', '〼'), ('ぁ', 'ゖ'), ('\u{3099}', 'ゟ'), - ('ァ', 'ヺ'), - ('ー', 'ヿ'), + ('ァ', 'ヿ'), ('ㄅ', 'ㄯ'), ('ㄱ', 'ㆎ'), ('ㆠ', 'ㆿ'), @@ -441,7 +441,7 @@ pub mod derived_property { ('A', 'Z'), ('_', '_'), ('a', 'z'), - ('ヲ', 'ᄒ'), + ('・', 'ᄒ'), ('ᅡ', 'ᅦ'), ('ᅧ', 'ᅬ'), ('ᅭ', 'ᅲ'), @@ -782,6 +782,7 @@ pub mod derived_property { ('𫝀', '𫠝'), ('𫠠', '𬺡'), ('𬺰', '𮯠'), + ('\u{2ebf0}', '\u{2ee5d}'), ('丽', '𪘀'), ('𰀀', '𱍊'), ('𱍐', '𲎯'), @@ -1447,6 +1448,7 @@ pub mod derived_property { ('𫝀', '𫠝'), ('𫠠', '𬺡'), ('𬺰', '𮯠'), + ('\u{2ebf0}', '\u{2ee5d}'), ('丽', '𪘀'), ('𰀀', '𱍊'), ('𱍐', '𲎯'), diff --git a/xtask/codegen/css.ungram b/xtask/codegen/css.ungram index 90b388d7d607..d9cc0efebd59 100644 --- a/xtask/codegen/css.ungram +++ b/xtask/codegen/css.ungram @@ -399,15 +399,17 @@ CssBlock = declaration_list: CssDeclarationList '}' - -CssDeclarationList = CssDeclaration* +CssDeclarationList = CssDeclaration (';' CssDeclaration)* ';'? CssDeclaration = - name: CssIdentifier | CssCustomProperty + name: AnyCssDeclarationName ':' - value: AnyCssValue + value: CssListOfComponentValues important: CssDeclarationImportant? +CssListOfComponentValues = AnyCssValue* + +AnyCssDeclarationName = CssIdentifier | CssCustomProperty CssDeclarationImportant = '!' @@ -565,7 +567,7 @@ AnyCssValue = CssIdentifier | CssString | CssNumber - | CssDimension + | AnyCssDimension | CssRatio | CssAnyFunction | CssCustomProperty @@ -575,14 +577,20 @@ AnyCssValue = // 100vh // 4rem // 1e-2 -CssDimension = +AnyCssDimension = + CssRegularDimension | CssPercentage + +CssRegularDimension = value: CssNumber unit: CssIdentifier - +CssPercentDimension = + value: CssNumber + unit: '%' // 3 / 2 CssRatio = numerator: CssNumber + '/' denominator: CssNumber @@ -614,11 +622,15 @@ CssPercentage = // parsed with --ident -CssCustomProperty = value: 'css_custom_property' +CssCustomProperty = value: CssIdentifier -CssParameterList = CssParameter* +CssParameterList = CssParameter (',' CssParameter)* ','? -CssParameter = AnyCssValue +// cubic-bezier(0.1, 0.7, 1.0, 0.1) +// ^^^ +// repeating-radial-gradient(red, yellow 10%, green 15%); +// ^^^^^^^^^^ +CssParameter = CssListOfComponentValues CssIdentifier = value: 'ident' CssString = value: 'css_string_literal' diff --git a/xtask/codegen/src/css_kinds_src.rs b/xtask/codegen/src/css_kinds_src.rs index 050c0d56cf37..8e117247ef85 100644 --- a/xtask/codegen/src/css_kinds_src.rs +++ b/xtask/codegen/src/css_kinds_src.rs @@ -253,7 +253,6 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_ANY_FUNCTION", "CSS_BLOCK", "CSS_DECLARATION", - "CSS_DIMENSION", "CSS_IDENTIFIER", "CSS_KEYFRAMES_BLOCK", "CSS_KEYFRAMES_SELECTOR", @@ -267,9 +266,13 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_VAR_FUNCTION_VALUE", "CSS_ATTRIBUTE_LIST", "CSS_DECLARATION_LIST", + "CSS_LIST_OF_COMPONENT_VALUES", "CSS_KEYFRAMES_SELECTOR_LIST", "CSS_PARAMETER_LIST", "CSS_DECLARATION_IMPORTANT", + "CSS_UNIT", + "CSS_PERCENT_DIMENSION", + "CSS_REGULAR_DIMENSION", // Selectors nodes "CSS_NAMESPACE", "CSS_NAMED_NAMESPACE_PREFIX", @@ -339,5 +342,8 @@ pub const CSS_KINDS_SRC: KindsSrc = KindsSrc { "CSS_BOGUS_PSEUDO_CLASS", "CSS_BOGUS_PSEUDO_ELEMENT", "CSS_BOGUS_AT_RULE", + "CSS_BOGUS_DECLARATION_ITEM", + "CSS_BOGUS_COMPONENT_VALUE", + "CSS_BOGUS_PARAMETER", ], };