Skip to main content

oxc_css_parser/parser/at_rule/
custom_selector.rs

1use super::Parser;
2use crate::{Parse, ast::*, error::PResult, pos::Span, tokenizer::Token, util};
3
4impl<'a> Parse<'a> for CustomSelector<'a> {
5    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
6        let prefix_arg = if matches!(input.cursor.peek()?.token, Token::DollarVar(..)) {
7            Some(input.parse::<CustomSelectorArg>()?)
8        } else {
9            None
10        };
11
12        let (_, colon_span) = input.cursor.expect_colon()?;
13        if let Some(prefix_arg) = &prefix_arg {
14            util::assert_no_ws_or_comment(&prefix_arg.span, &colon_span)?;
15        }
16        let name = input.parse::<Ident>()?;
17        util::assert_no_ws_or_comment(&colon_span, &name.span)?;
18
19        let args = if matches!(input.cursor.peek()?.token, Token::LParen(..)) {
20            Some(input.parse::<CustomSelectorArgs>()?)
21        } else {
22            None
23        };
24
25        let span = Span {
26            start: prefix_arg
27                .as_ref()
28                .map(|prefix_arg| prefix_arg.span.start)
29                .unwrap_or(name.span.start),
30            end: args.as_ref().map(|args| args.span.end).unwrap_or(name.span.end),
31        };
32        Ok(CustomSelector { prefix_arg, name, args, span })
33    }
34}
35
36impl<'a> Parse<'a> for CustomSelectorArg<'a> {
37    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
38        let (dollar_var, dollar_var_span) = input.cursor.expect_dollar_var()?;
39        Ok(CustomSelectorArg {
40            name: input.ident(
41                dollar_var.ident,
42                Span { start: dollar_var_span.start + 1, end: dollar_var_span.end },
43            ),
44            span: dollar_var_span,
45        })
46    }
47}
48
49impl<'a> Parse<'a> for CustomSelectorArgs<'a> {
50    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
51        let (_, Span { start, .. }) = input.cursor.expect_l_paren()?;
52
53        let mut args = input.vec();
54        let mut comma_spans = input.vec();
55        while !matches!(input.cursor.peek()?.token, Token::RParen(..)) {
56            args.push(input.parse()?);
57            if !matches!(input.cursor.peek()?.token, Token::RParen(..)) {
58                comma_spans.push(input.cursor.expect_comma()?.1);
59            }
60        }
61
62        let (_, Span { end, .. }) = input.cursor.expect_r_paren()?;
63        Ok(CustomSelectorArgs { args, comma_spans, span: Span { start, end } })
64    }
65}
66
67// https://drafts.csswg.org/css-extensions/#custom-selectors
68impl<'a> Parse<'a> for CustomSelectorPrelude<'a> {
69    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
70        let custom_selector = input.parse::<CustomSelector>()?;
71        let selector = input.parse::<SelectorList>()?;
72        let span = Span { start: custom_selector.span.start, end: selector.span.end };
73        Ok(CustomSelectorPrelude { custom_selector, selector, span })
74    }
75}