use super::parse_error::{
expected_any_font_family_name, expected_any_font_feature_value_item,
expected_font_feature_values_item,
};
use crate::syntax::block::{parse_declaration_block, ParseBlockBody};
use crate::syntax::is_at_string;
use crate::{
lexer::CssLexContext,
parser::CssParser,
syntax::{
is_at_identifier, parse_custom_identifier,
parse_error::expected_non_css_wide_keyword_identifier, parse_string,
},
};
use biome_css_syntax::{
CssSyntaxKind::{self, *},
T,
};
use biome_parser::parse_lists::ParseSeparatedList;
use biome_parser::parse_recovery::ParseRecovery;
use biome_parser::{
parse_lists::ParseNodeList,
parse_recovery::{ParseRecoveryTokenSet, RecoveryResult},
parsed_syntax::ParsedSyntax::{self, Absent, Present},
prelude::*,
Parser, TokenSet,
};
#[inline]
pub(crate) fn is_at_font_feature_values_at_rule(p: &mut CssParser) -> bool {
p.at(T![font_feature_values])
}
#[inline]
pub(crate) fn parse_font_feature_values_at_rule(p: &mut CssParser) -> ParsedSyntax {
if !is_at_font_feature_values_at_rule(p) {
return Absent;
}
let m = p.start();
p.bump(T![font_feature_values]);
CssFontFamilyNameList.parse_list(p);
FontFeatureValuesBlock.parse_block_body(p);
Present(m.complete(p, CSS_FONT_FEATURE_VALUES_AT_RULE))
}
struct CssFontFamilyNameList;
impl ParseSeparatedList for CssFontFamilyNameList {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const LIST_KIND: Self::Kind = CSS_FONT_FAMILY_NAME_LIST;
fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax {
parse_font_family_name(p)
}
fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['{'])
}
fn recover(
&mut self,
p: &mut Self::Parser<'_>,
parsed_element: ParsedSyntax,
) -> RecoveryResult {
parsed_element.or_recover(
p,
&CssFontFamilyNameListParseRecovery,
expected_any_font_family_name,
)
}
fn separating_element_kind(&mut self) -> Self::Kind {
T![,]
}
}
struct CssFontFamilyNameListParseRecovery;
impl ParseRecovery for CssFontFamilyNameListParseRecovery {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const RECOVERED_KIND: Self::Kind = CSS_BOGUS_FONT_FAMILY_NAME;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['{']) || p.at(T![,]) || is_at_font_family_name(p)
}
}
#[inline]
pub(crate) fn is_at_font_family_name(p: &mut CssParser) -> bool {
is_at_identifier(p) || is_at_string(p)
}
#[inline]
pub(crate) fn parse_font_family_name(p: &mut CssParser) -> ParsedSyntax {
if !is_at_font_family_name(p) {
return Absent;
}
if is_at_string(p) {
parse_string(p)
} else {
let m = p.start();
CssCustomIdentifierList.parse_list(p);
Present(m.complete(p, CSS_FONT_FAMILY_NAME))
}
}
struct CssCustomIdentifierList;
impl ParseNodeList for CssCustomIdentifierList {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const LIST_KIND: Self::Kind = CSS_CUSTOM_IDENTIFIER_LIST;
fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax {
parse_custom_identifier(p, CssLexContext::Regular)
}
fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['{']) || p.at(T![,])
}
fn recover(
&mut self,
p: &mut Self::Parser<'_>,
parsed_element: ParsedSyntax,
) -> RecoveryResult {
parsed_element.or_recover(
p,
&CssCustomIdentifierListParseRecovery,
expected_non_css_wide_keyword_identifier,
)
}
}
struct CssCustomIdentifierListParseRecovery;
impl ParseRecovery for CssCustomIdentifierListParseRecovery {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const RECOVERED_KIND: Self::Kind = CSS_BOGUS_CUSTOM_IDENTIFIER;
fn is_at_recovered(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['{']) || p.at(T![,]) || is_at_identifier(p)
}
}
struct FontFeatureValuesBlock;
impl ParseBlockBody for FontFeatureValuesBlock {
const BLOCK_KIND: CssSyntaxKind = CSS_FONT_FEATURE_VALUES_BLOCK;
fn is_at_element(&self, p: &mut CssParser) -> bool {
p.at(T![@])
}
fn parse_list(&mut self, p: &mut CssParser) {
FontFeatureValuesItemList.parse_list(p);
}
}
struct FontFeatureValuesItemList;
impl ParseNodeList for FontFeatureValuesItemList {
type Kind = CssSyntaxKind;
type Parser<'source> = CssParser<'source>;
const LIST_KIND: Self::Kind = CSS_FONT_FEATURE_VALUES_ITEM_LIST;
fn parse_element(&mut self, p: &mut Self::Parser<'_>) -> ParsedSyntax {
parse_font_feature_values_item(p)
}
fn is_at_list_end(&self, p: &mut Self::Parser<'_>) -> bool {
p.at(T!['}'])
}
fn recover(
&mut self,
p: &mut Self::Parser<'_>,
parsed_element: ParsedSyntax,
) -> RecoveryResult {
parsed_element.or_recover_with_token_set(
p,
&ParseRecoveryTokenSet::new(
CSS_BOGUS_FONT_FEATURE_VALUES_ITEM,
FONT_FEATURE_VALUES_ITEM_LIST_RECOVERY_SET,
),
expected_font_feature_values_item,
)
}
}
#[inline]
fn parse_font_feature_values_item(p: &mut CssParser) -> ParsedSyntax {
if !p.at(T![@]) {
return Absent;
}
let m = p.start();
p.bump(T![@]);
if !p.eat_ts(FONT_FEATURE_VALUES_ITEM_SET) {
p.error(expected_any_font_feature_value_item(p, p.cur_range()))
}
parse_declaration_block(p);
Present(m.complete(p, CSS_FONT_FEATURE_VALUES_ITEM))
}
const FONT_FEATURE_VALUES_ITEM_SET: TokenSet<CssSyntaxKind> = token_set![
T![stylistic],
T![historical_forms],
T![styleset],
T![character_variant],
T![swash],
T![ornaments],
T![annotation]
];
const FONT_FEATURE_VALUES_ITEM_LIST_RECOVERY_SET: TokenSet<CssSyntaxKind> =
FONT_FEATURE_VALUES_ITEM_SET.union(token_set![T!['}']]);