1use super::prelude::*;
2use crate::{MozPseudoElement, MsPseudoElement, OPseudoElement, WebkitPseudoElement};
3
4macro_rules! apply_pseudo_element {
5 ($macro: ident) => {
6 $macro! {
7 After: CssAtomSet::After,
8 Backdrop: CssAtomSet::Backdrop,
9 Before: CssAtomSet::Before,
10 Checkmark: CssAtomSet::Checkmark,
11 ClearIcon: CssAtomSet::ClearIcon,
12 ColorSwatch: CssAtomSet::ColorSwatch,
13 Column: CssAtomSet::Column,
14 Cue: CssAtomSet::Cue,
15 CueRegion: CssAtomSet::CueRegion,
16 DetailsContent: CssAtomSet::DetailsContent,
17 FieldComponent: CssAtomSet::FieldComponent,
18 FieldSeparator: CssAtomSet::FieldSeparator,
19 FieldText: CssAtomSet::FieldText,
20 FileSelectorButton: CssAtomSet::FileSelectorButton,
21 FirstLetter: CssAtomSet::FirstLetter,
22 FirstLine: CssAtomSet::FirstLine,
23 GrammarError: CssAtomSet::GrammarError,
24 InterestButton: CssAtomSet::InterestButton,
25 Marker: CssAtomSet::Marker,
26 PickerIcon: CssAtomSet::PickerIcon,
27 Placeholder: CssAtomSet::Placeholder,
28 RevealIcon: CssAtomSet::RevealIcon,
29 ScrollMarker: CssAtomSet::ScrollMarker,
30 ScrollMarkerGroup: CssAtomSet::ScrollMarkerGroup,
31 Selection: CssAtomSet::Selection,
32 SpellingError: CssAtomSet::SpellingError,
33 SearchText: CssAtomSet::SearchText,
34 SliderFill: CssAtomSet::SliderFill,
35 SliderThumb: CssAtomSet::SliderThumb,
36 SliderTrack: CssAtomSet::SliderTrack,
37 StepControl: CssAtomSet::StepControl,
38 StepDown: CssAtomSet::StepDown,
39 StepUp: CssAtomSet::StepUp,
40 TargetText: CssAtomSet::TargetText,
41 ViewTransition: CssAtomSet::ViewTransition,
42 }
43 };
44}
45
46macro_rules! define_pseudo_element {
47 ( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
48 #[node]
49 #[derive(ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
50 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
51 #[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.selectors"))]
52 #[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
53#[derive(csskit_derives::NodeWithMetadata)]
54 pub enum PseudoElement {
55 $($(#[$meta])* $ident(#[semantic_eq(skip)] T![::], T![Ident]),)+
56 Webkit(WebkitPseudoElement),
57 Moz(MozPseudoElement),
58 Ms(MsPseudoElement),
59 O(OPseudoElement),
60 }
61 };
62}
63apply_pseudo_element!(define_pseudo_element);
64
65impl<'a> Peek<'a> for PseudoElement {
66 const PEEK_KINDSET: KindSet = KindSet::new(&[Kind::Colon]);
67
68 #[inline(always)]
69 fn peek<I>(p: &Parser<'a, I>, _: css_lexer::Cursor) -> bool
70 where
71 I: Iterator<Item = Cursor> + Clone,
72 {
73 p.peek::<T![::]>() && p.peek_n(3) == Kind::Ident
74 }
75}
76
77impl<'a> Parse<'a> for PseudoElement {
78 fn parse<I>(p: &mut Parser<'a, I>) -> ParserResult<Self>
79 where
80 I: Iterator<Item = Cursor> + Clone,
81 {
82 let c = p.peek_n(3);
83 macro_rules! match_keyword {
84 ( $($(#[$meta:meta])* $ident: ident: $pat: pat $(,)*)+ ) => {
85 match p.to_atom::<CssAtomSet>(c) {
86 $($pat => {
87 let skip = p.set_skip(KindSet::NONE);
88 let colons = p.parse::<T![::]>();
89 let ident = p.parse::<T![Ident]>();
90 p.set_skip(skip);
91 Ok(Self::$ident(colons?, ident?))
92 })+
93 _ => {
94 if let Ok(psuedo) = p.try_parse::<WebkitPseudoElement>() {
95 return Ok(Self::Webkit(psuedo));
96 }
97 if let Ok(psuedo) = p.try_parse::<MozPseudoElement>() {
98 return Ok(Self::Moz(psuedo));
99 }
100 if let Ok(psuedo) = p.try_parse::<MsPseudoElement>() {
101 return Ok(Self::Ms(psuedo));
102 }
103 if let Ok(psuedo) = p.try_parse::<OPseudoElement>() {
104 return Ok(Self::O(psuedo));
105 }
106 Err(Diagnostic::new(c, Diagnostic::unexpected_pseudo_element))?
107 }
108 }
109 }
110 }
111 apply_pseudo_element!(match_keyword)
112 }
113}
114
115pseudo_class!(
116 #[node]
117 #[derive(ToSpan, ToCursors, SemanticEq, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
118 #[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
119 #[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit(self))]
120#[derive(csskit_derives::NodeWithMetadata)]
121 #[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.selectors"))]
122 pub enum LegacyPseudoElement {
123 After: CssAtomSet::After,
124 Before: CssAtomSet::Before,
125 FirstLetter: CssAtomSet::FirstLetter,
126 FirstLine: CssAtomSet::FirstLine,
127 }
128);
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133 use crate::CssAtomSet;
134 use css_parse::assert_parse;
135
136 #[test]
137 fn test_writes() {
138 assert_parse!(CssAtomSet::ATOMS, PseudoElement, "::after");
139 assert_parse!(CssAtomSet::ATOMS, PseudoElement, "::first-letter");
140 assert_parse!(CssAtomSet::ATOMS, PseudoElement, "::view-transition");
141 assert_parse!(CssAtomSet::ATOMS, LegacyPseudoElement, ":after");
142 }
143
144 #[cfg(feature = "css_feature_data")]
145 #[test]
146 fn test_feature_data() {
147 use crate::assert_feature_id;
148 assert_feature_id!("::after", PseudoElement, "css.selectors.after");
149 assert_feature_id!("::view-transition", PseudoElement, "css.selectors.view-transition");
150 assert_feature_id!("::spelling-error", PseudoElement, "css.selectors.spelling-error");
151 assert_feature_id!(":after", LegacyPseudoElement, "css.selectors.after");
152 assert_feature_id!(":before", LegacyPseudoElement, "css.selectors.before");
153 }
154}