1 use std::{
2 collections::HashSet,
3 fmt::{self, Display, Write},
4 hash::{Hash, Hasher},
5 sync::atomic::{AtomicU32, Ordering as AtomicOrdering},
6 };
7
8 use crate::error::SassResult;
9
10 use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity};
11
12 pub(crate) static COMPLEX_SELECTOR_UNIQUE_ID: AtomicU32 = AtomicU32::new(0);
13
14 #[derive(Clone, Debug)]
15 pub(crate) struct ComplexSelectorHashSet(HashSet<u32>);
16
17 impl ComplexSelectorHashSet {
new() -> Self18 pub fn new() -> Self {
19 Self(HashSet::new())
20 }
21
insert(&mut self, complex: &ComplexSelector) -> bool22 pub fn insert(&mut self, complex: &ComplexSelector) -> bool {
23 self.0.insert(complex.unique_id)
24 }
25
contains(&self, complex: &ComplexSelector) -> bool26 pub fn contains(&self, complex: &ComplexSelector) -> bool {
27 self.0.contains(&complex.unique_id)
28 }
29
extend<'a>(&mut self, complexes: impl Iterator<Item = &'a ComplexSelector>)30 pub fn extend<'a>(&mut self, complexes: impl Iterator<Item = &'a ComplexSelector>) {
31 self.0.extend(complexes.map(|complex| complex.unique_id));
32 }
33 }
34
35 /// A complex selector.
36 ///
37 /// A complex selector is composed of `CompoundSelector`s separated by
38 /// `Combinator`s. It selects elements based on their parent selectors.
39 #[derive(Clone, Debug)]
40 pub(crate) struct ComplexSelector {
41 /// The components of this selector.
42 ///
43 /// This is never empty.
44 ///
45 /// Descendant combinators aren't explicitly represented here. If two
46 /// `CompoundSelector`s are adjacent to one another, there's an implicit
47 /// descendant combinator between them.
48 ///
49 /// It's possible for multiple `Combinator`s to be adjacent to one another.
50 /// This isn't valid CSS, but Sass supports it for CSS hack purposes.
51 pub components: Vec<ComplexSelectorComponent>,
52
53 /// Whether a line break should be emitted *before* this selector.
54 pub line_break: bool,
55
56 /// A unique identifier for this complex selector. Used to perform a pointer
57 /// equality check, like would be done for objects in a language like JavaScript
58 /// or dart
59 unique_id: u32,
60 }
61
62 impl PartialEq for ComplexSelector {
eq(&self, other: &Self) -> bool63 fn eq(&self, other: &Self) -> bool {
64 self.components == other.components
65 }
66 }
67
68 impl Eq for ComplexSelector {}
69
70 impl Hash for ComplexSelector {
hash<H: Hasher>(&self, state: &mut H)71 fn hash<H: Hasher>(&self, state: &mut H) {
72 self.components.hash(state);
73 }
74 }
75
76 impl fmt::Display for ComplexSelector {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result77 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
78 let mut last_component = None;
79
80 for component in &self.components {
81 if let Some(c) = last_component {
82 if !omit_spaces_around(c) && !omit_spaces_around(component) {
83 f.write_char(' ')?;
84 }
85 }
86 write!(f, "{}", component)?;
87 last_component = Some(component);
88 }
89 Ok(())
90 }
91 }
92
93 /// When `style` is `OutputStyle::compressed`, omit spaces around combinators.
omit_spaces_around(component: &ComplexSelectorComponent) -> bool94 fn omit_spaces_around(component: &ComplexSelectorComponent) -> bool {
95 // todo: compressed
96 let is_compressed = false;
97 is_compressed && matches!(component, ComplexSelectorComponent::Combinator(..))
98 }
99
100 impl ComplexSelector {
new(components: Vec<ComplexSelectorComponent>, line_break: bool) -> Self101 pub fn new(components: Vec<ComplexSelectorComponent>, line_break: bool) -> Self {
102 Self {
103 components,
104 line_break,
105 unique_id: COMPLEX_SELECTOR_UNIQUE_ID.fetch_add(1, AtomicOrdering::Relaxed),
106 }
107 }
108
max_specificity(&self) -> i32109 pub fn max_specificity(&self) -> i32 {
110 self.specificity().min
111 }
112
min_specificity(&self) -> i32113 pub fn min_specificity(&self) -> i32 {
114 self.specificity().max
115 }
116
specificity(&self) -> Specificity117 pub fn specificity(&self) -> Specificity {
118 let mut min = 0;
119 let mut max = 0;
120 for component in &self.components {
121 if let ComplexSelectorComponent::Compound(compound) = component {
122 min += compound.min_specificity();
123 max += compound.max_specificity();
124 }
125 }
126 Specificity::new(min, max)
127 }
128
is_invisible(&self) -> bool129 pub fn is_invisible(&self) -> bool {
130 self.components
131 .iter()
132 .any(ComplexSelectorComponent::is_invisible)
133 }
134
135 /// Returns whether `self` is a superselector of `other`.
136 ///
137 /// That is, whether `self` matches every element that `other` matches, as well
138 /// as possibly additional elements.
is_super_selector(&self, other: &Self) -> bool139 pub fn is_super_selector(&self, other: &Self) -> bool {
140 if let Some(ComplexSelectorComponent::Combinator(..)) = self.components.last() {
141 return false;
142 }
143 if let Some(ComplexSelectorComponent::Combinator(..)) = other.components.last() {
144 return false;
145 }
146
147 let mut i1 = 0;
148 let mut i2 = 0;
149
150 loop {
151 let remaining1 = self.components.len() - i1;
152 let remaining2 = other.components.len() - i2;
153
154 if remaining1 == 0 || remaining2 == 0 || remaining1 > remaining2 {
155 return false;
156 }
157
158 let compound1 = match self.components.get(i1) {
159 Some(ComplexSelectorComponent::Compound(c)) => c,
160 Some(ComplexSelectorComponent::Combinator(..)) => return false,
161 None => unreachable!(),
162 };
163
164 if let ComplexSelectorComponent::Combinator(..) = other.components[i2] {
165 return false;
166 }
167
168 if remaining1 == 1 {
169 let parents = other
170 .components
171 .iter()
172 .take(other.components.len() - 1)
173 .skip(i2)
174 .cloned()
175 .collect();
176 return compound1.is_super_selector(
177 other.components.last().unwrap().as_compound(),
178 &Some(parents),
179 );
180 }
181
182 let mut after_super_selector = i2 + 1;
183 while after_super_selector < other.components.len() {
184 if let Some(ComplexSelectorComponent::Compound(compound2)) =
185 other.components.get(after_super_selector - 1)
186 {
187 if compound1.is_super_selector(
188 compound2,
189 &Some(
190 other
191 .components
192 .iter()
193 .take(after_super_selector - 1)
194 .skip(i2 + 1)
195 .cloned()
196 .collect(),
197 ),
198 ) {
199 break;
200 }
201 }
202
203 after_super_selector += 1;
204 }
205
206 if after_super_selector == other.components.len() {
207 return false;
208 }
209
210 if let Some(ComplexSelectorComponent::Combinator(combinator1)) =
211 self.components.get(i1 + 1)
212 {
213 let combinator2 = match other.components.get(after_super_selector) {
214 Some(ComplexSelectorComponent::Combinator(c)) => c,
215 Some(ComplexSelectorComponent::Compound(..)) => return false,
216 None => unreachable!(),
217 };
218
219 if combinator1 == &Combinator::FollowingSibling {
220 if combinator2 == &Combinator::Child {
221 return false;
222 }
223 } else if combinator1 != combinator2 {
224 return false;
225 }
226
227 if remaining1 == 3 && remaining2 > 3 {
228 return false;
229 }
230
231 i1 += 2;
232 i2 = after_super_selector + 1;
233 } else if let Some(ComplexSelectorComponent::Combinator(combinator2)) =
234 other.components.get(after_super_selector)
235 {
236 if combinator2 != &Combinator::Child {
237 return false;
238 }
239 i1 += 1;
240 i2 = after_super_selector + 1;
241 } else {
242 i1 += 1;
243 i2 = after_super_selector;
244 }
245 }
246 }
247
contains_parent_selector(&self) -> bool248 pub fn contains_parent_selector(&self) -> bool {
249 self.components.iter().any(|c| {
250 if let ComplexSelectorComponent::Compound(compound) = c {
251 compound.components.iter().any(|simple| {
252 if simple.is_parent() {
253 return true;
254 }
255 if let SimpleSelector::Pseudo(Pseudo {
256 selector: Some(sel),
257 ..
258 }) = simple
259 {
260 return sel.contains_parent_selector();
261 }
262 false
263 })
264 } else {
265 false
266 }
267 })
268 }
269 }
270
271 #[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)]
272 pub(crate) enum Combinator {
273 /// Matches the right-hand selector if it's immediately adjacent to the
274 /// left-hand selector in the DOM tree.
275 ///
276 /// `'+'`
277 NextSibling,
278
279 /// Matches the right-hand selector if it's a direct child of the left-hand
280 /// selector in the DOM tree.
281 ///
282 /// `'>'`
283 Child,
284
285 /// Matches the right-hand selector if it comes after the left-hand selector
286 /// in the DOM tree.
287 ///
288 /// `'~'`
289 FollowingSibling,
290 }
291
292 impl Display for Combinator {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result293 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294 f.write_char(match self {
295 Self::NextSibling => '+',
296 Self::Child => '>',
297 Self::FollowingSibling => '~',
298 })
299 }
300 }
301
302 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
303 pub(crate) enum ComplexSelectorComponent {
304 Combinator(Combinator),
305 Compound(CompoundSelector),
306 }
307
308 impl ComplexSelectorComponent {
is_invisible(&self) -> bool309 pub fn is_invisible(&self) -> bool {
310 match self {
311 Self::Combinator(..) => false,
312 Self::Compound(c) => c.is_invisible(),
313 }
314 }
315
is_compound(&self) -> bool316 pub fn is_compound(&self) -> bool {
317 matches!(self, Self::Compound(..))
318 }
319
is_combinator(&self) -> bool320 pub fn is_combinator(&self) -> bool {
321 matches!(self, Self::Combinator(..))
322 }
323
resolve_parent_selectors( self, parent: SelectorList, ) -> SassResult<Option<Vec<ComplexSelector>>>324 pub fn resolve_parent_selectors(
325 self,
326 parent: SelectorList,
327 ) -> SassResult<Option<Vec<ComplexSelector>>> {
328 match self {
329 Self::Compound(c) => c.resolve_parent_selectors(parent),
330 Self::Combinator(..) => todo!(),
331 }
332 }
333
as_compound(&self) -> &CompoundSelector334 pub fn as_compound(&self) -> &CompoundSelector {
335 match self {
336 Self::Compound(c) => c,
337 Self::Combinator(..) => unreachable!(),
338 }
339 }
340 }
341
342 impl Display for ComplexSelectorComponent {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result343 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344 match self {
345 Self::Compound(c) => write!(f, "{}", c),
346 Self::Combinator(c) => write!(f, "{}", c),
347 }
348 }
349 }
350