Skip to main content

css_ast/rules/
scope.rs

1use super::prelude::*;
2use crate::selector::ForgivingSelector;
3use css_parse::Box;
4
5/// Represents the `@scope` at-rule, e.g. `@scope (.card) to (.content) { p { color: red } }`.
6///
7/// ```md
8/// <scope-rule>
9///  │├─ "@scope" ─╮─────────────────────────────────────────╭─╮────────────────────────────────────────────────╭─ <block> ─┤│
10///                ╰─ "(" ─ <forgiving-selector-list> ─ ")" ─╯ ╰─ "to" ─ "(" ─ <forgiving-selector-list> ─ ")" ─╯
11/// ```
12///
13/// <https://drafts.csswg.org/css-cascade-6/#at-ruledef-scope>
14#[node]
15#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
17#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable), visit)]
18#[cfg_attr(feature = "css_feature_data", derive(::csskit_derives::ToCSSFeature), css_feature("css.at-rules.scope"))]
19#[derive(csskit_derives::NodeWithMetadata)]
20#[metadata(node_kinds = AtRule, used_at_rules = Scope)]
21pub struct ScopeRule<'a> {
22	#[cfg_attr(feature = "visitable", visit(skip))]
23	#[atom(CssAtomSet::Scope)]
24	pub name: T![AtKeyword],
25	#[metadata(prelude)]
26	pub start: Option<Box<'a, ScopeStart<'a>>>,
27	#[metadata(prelude)]
28	pub end: Option<Box<'a, ScopeEnd<'a>>>,
29	#[metadata(block)]
30	pub block: ScopeRuleBlock<'a>,
31}
32
33#[node]
34#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
36#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
37#[derive(csskit_derives::NodeWithMetadata)]
38pub struct ScopeStart<'a>(
39	#[cfg_attr(feature = "visitable", visit(skip))]
40	#[semantic_eq(skip)]
41	pub T!['('],
42	pub Option<ForgivingSelector<'a>>,
43	#[cfg_attr(feature = "visitable", visit(skip))]
44	#[semantic_eq(skip)]
45	pub T![')'],
46);
47
48#[node]
49#[derive(Parse, Peek, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
50#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
51#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
52#[derive(csskit_derives::NodeWithMetadata)]
53pub struct ScopeEnd<'a>(
54	#[cfg_attr(feature = "visitable", visit(skip))]
55	#[atom(CssAtomSet::To)]
56	pub T![Ident],
57	#[cfg_attr(feature = "visitable", visit(skip))]
58	#[semantic_eq(skip)]
59	pub T!['('],
60	pub Option<ForgivingSelector<'a>>,
61	#[cfg_attr(feature = "visitable", visit(skip))]
62	#[semantic_eq(skip)]
63	pub T![')'],
64);
65
66#[node]
67#[derive(Parse, ToCursors, ToSpan, SemanticEq, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
68#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
69#[cfg_attr(feature = "visitable", derive(csskit_derives::Visitable))]
70#[derive(csskit_derives::NodeWithMetadata)]
71pub struct ScopeRuleBlock<'a>(pub Block<'a, StyleValue<'a>, Rule<'a>, CssMetadata>);
72
73#[cfg(test)]
74mod tests {
75	use super::*;
76	use crate::CssAtomSet;
77	use css_parse::assert_parse;
78
79	#[test]
80	fn test_writes() {
81		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope{}");
82		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope(.card){}");
83		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope(.card) to (.content){}");
84		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope to (.content){}");
85		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope(.card,.tile) to (.content>img){}");
86		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope(.card){color:black}");
87		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope(.card){img{color:black}}");
88		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope(){}");
89		assert_parse!(CssAtomSet::ATOMS, ScopeRule, "@scope{img{color:black}}");
90	}
91}