Skip to main content

oxc_css_parser/parser/at_rule/
custom_selector.rs

1use super::Parser;
2use crate::{Parse, ast::*, error::PResult, expect, peek, pos::Span, tokenizer::Token, util};
3
4impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for CustomSelector<'s> {
5    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
6        let prefix_arg = if matches!(peek!(input).token, Token::DollarVar(..)) {
7            Some(input.parse::<CustomSelectorArg>()?)
8        } else {
9            None
10        };
11
12        let (_, colon_span) = expect!(input, 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!(peek!(input).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
31                .as_ref()
32                .map(|args| args.span.end)
33                .unwrap_or(name.span.end),
34        };
35        Ok(CustomSelector {
36            prefix_arg,
37            name,
38            args,
39            span,
40        })
41    }
42}
43
44impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for CustomSelectorArg<'s> {
45    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
46        let (dollar_var, dollar_var_span) = expect!(input, DollarVar);
47        Ok(CustomSelectorArg {
48            name: (
49                dollar_var.ident,
50                Span {
51                    start: dollar_var_span.start + 1,
52                    end: dollar_var_span.end,
53                },
54            )
55                .into(),
56            span: dollar_var_span,
57        })
58    }
59}
60
61impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for CustomSelectorArgs<'s> {
62    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
63        let (_, Span { start, .. }) = expect!(input, LParen);
64
65        let mut args = vec![];
66        let mut comma_spans = vec![];
67        while !matches!(peek!(input).token, Token::RParen(..)) {
68            args.push(input.parse()?);
69            if !matches!(peek!(input).token, Token::RParen(..)) {
70                comma_spans.push(expect!(input, Comma).1);
71            }
72        }
73
74        let (_, Span { end, .. }) = expect!(input, RParen);
75        Ok(CustomSelectorArgs {
76            args,
77            comma_spans,
78            span: Span { start, end },
79        })
80    }
81}
82
83// https://drafts.csswg.org/css-extensions/#custom-selectors
84impl<'cmt, 's: 'cmt> Parse<'cmt, 's> for CustomSelectorPrelude<'s> {
85    fn parse(input: &mut Parser<'cmt, 's>) -> PResult<Self> {
86        let custom_selector = input.parse::<CustomSelector>()?;
87        let selector = input.parse::<SelectorList>()?;
88        let span = Span {
89            start: custom_selector.span.start,
90            end: selector.span.end,
91        };
92        Ok(CustomSelectorPrelude {
93            custom_selector,
94            selector,
95            span,
96        })
97    }
98}