Skip to main content

oxc_css_parser/parser/at_rule/
page.rs

1use super::Parser;
2use crate::{
3    Parse,
4    ast::*,
5    error::PResult,
6    pos::Span,
7    tokenizer::{Token, TokenWithSpan},
8    util,
9};
10
11// https://www.w3.org/TR/css-page-3/#syntax-page-selector
12//
13// <page-selector> = [ <ident-token>? <pseudo-page>* ]!
14impl<'a> Parse<'a> for PageSelector<'a> {
15    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
16        let mut name = None;
17        let mut pseudo = input.vec();
18        let start;
19        let mut end;
20
21        if let Token::Colon(..) | Token::ColonColon(..) = &input.cursor.peek()?.token {
22            let first = input.parse::<PseudoPage>()?;
23            start = first.span.start;
24            end = first.span.end;
25            pseudo.push(first);
26        } else {
27            let ident = input.parse::<InterpolableIdent>()?;
28            let ident_span = ident.span();
29            start = ident_span.start;
30            end = ident_span.end;
31            name = Some(ident)
32        }
33
34        loop {
35            match input.cursor.peek()? {
36                TokenWithSpan { token: Token::Colon(..) | Token::ColonColon(..), span }
37                    if span.start == end =>
38                {
39                    let item = input.parse::<PseudoPage>()?;
40                    end = item.span.end;
41                    pseudo.push(item);
42                }
43                _ => break,
44            }
45        }
46
47        Ok(PageSelector { name, pseudo, span: Span { start, end } })
48    }
49}
50
51// <page-selector-list> = <page-selector>#
52impl<'a> Parse<'a> for PageSelectorList<'a> {
53    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
54        let first = input.parse::<PageSelector>()?;
55        let mut span = first.span;
56
57        let mut selectors = input.vec1(first);
58        let mut comma_spans = input.vec();
59        while let Some((_, comma_span)) = input.cursor.eat_comma()? {
60            comma_spans.push(comma_span);
61            selectors.push(input.parse()?);
62        }
63
64        if let Some(last) = selectors.last() {
65            span.end = last.span.end;
66        }
67        Ok(PageSelectorList { selectors, comma_spans, span })
68    }
69}
70
71// <pseudo-page> = ':' [ left | right | first | blank ]
72//
73// CSS Template Layout additionally uses a pseudo-element-style functional
74// form: `@page::slot(g)`.
75impl<'a> Parse<'a> for PseudoPage<'a> {
76    fn parse(input: &mut Parser<'a>) -> PResult<Self> {
77        let colon_span = match &input.cursor.peek()?.token {
78            Token::ColonColon(..) => input.cursor.expect_colon_colon()?.1,
79            _ => input.cursor.expect_colon()?.1,
80        };
81        let name = input.parse::<InterpolableIdent>()?;
82
83        let name_span = name.span();
84        util::assert_no_ws_or_comment(&colon_span, name_span)?;
85        let mut end = name_span.end;
86
87        let arg = match input.cursor.peek()? {
88            TokenWithSpan { token: Token::LParen(..), span } if span.start == end => {
89                input.cursor.bump()?;
90                let tokens = input.parse_tokens_in_parens()?;
91                end = input.cursor.expect_r_paren()?.1.end;
92                Some(tokens)
93            }
94            _ => None,
95        };
96
97        let span = Span { start: colon_span.start, end };
98        Ok(PseudoPage { name, arg, span })
99    }
100}