originals uses pointer equality check, rather than true equality
This commit is contained in:
parent
b44f81bd60
commit
fccf93cd96
@ -116,10 +116,7 @@ pub(crate) fn selector_append(args: CallArgs, parser: &mut Parser<'_>) -> SassRe
|
||||
}
|
||||
}];
|
||||
components.extend(complex.components.into_iter().skip(1));
|
||||
Ok(ComplexSelector {
|
||||
components,
|
||||
line_break: false,
|
||||
})
|
||||
Ok(ComplexSelector::new(components, false))
|
||||
} else {
|
||||
Err((format!("Can't append {} to {}.", complex, parent), span).into())
|
||||
}
|
||||
|
@ -1,12 +1,37 @@
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
fmt::{self, Display, Write},
|
||||
hash::{Hash, Hasher},
|
||||
sync::atomic::{AtomicU32, Ordering as AtomicOrdering},
|
||||
};
|
||||
|
||||
use crate::error::SassResult;
|
||||
|
||||
use super::{CompoundSelector, Pseudo, SelectorList, SimpleSelector, Specificity};
|
||||
|
||||
pub(crate) static COMPLEX_SELECTOR_UNIQUE_ID: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ComplexSelectorHashSet(HashSet<u32>);
|
||||
|
||||
impl ComplexSelectorHashSet {
|
||||
pub fn new() -> Self {
|
||||
Self(HashSet::new())
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, complex: &ComplexSelector) -> bool {
|
||||
self.0.insert(complex.unique_id)
|
||||
}
|
||||
|
||||
pub fn contains(&self, complex: &ComplexSelector) -> bool {
|
||||
self.0.contains(&complex.unique_id)
|
||||
}
|
||||
|
||||
pub fn extend<'a>(&mut self, complexes: impl Iterator<Item = &'a ComplexSelector>) {
|
||||
self.0.extend(complexes.map(|complex| complex.unique_id));
|
||||
}
|
||||
}
|
||||
|
||||
/// A complex selector.
|
||||
///
|
||||
/// A complex selector is composed of `CompoundSelector`s separated by
|
||||
@ -27,6 +52,11 @@ pub(crate) struct ComplexSelector {
|
||||
|
||||
/// Whether a line break should be emitted *before* this selector.
|
||||
pub line_break: bool,
|
||||
|
||||
/// A unique identifier for this complex selector. Used to perform a pointer
|
||||
/// equality check, like would be done for objects in a language like JavaScript
|
||||
/// or dart
|
||||
unique_id: u32,
|
||||
}
|
||||
|
||||
impl PartialEq for ComplexSelector {
|
||||
@ -68,6 +98,14 @@ fn omit_spaces_around(component: &ComplexSelectorComponent) -> bool {
|
||||
}
|
||||
|
||||
impl ComplexSelector {
|
||||
pub fn new(components: Vec<ComplexSelectorComponent>, line_break: bool) -> Self {
|
||||
Self {
|
||||
components,
|
||||
line_break,
|
||||
unique_id: COMPLEX_SELECTOR_UNIQUE_ID.fetch_add(1, AtomicOrdering::Relaxed),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn max_specificity(&self) -> i32 {
|
||||
self.specificity().min
|
||||
}
|
||||
|
@ -155,12 +155,12 @@ impl CompoundSelector {
|
||||
return Ok(Some(parent.components));
|
||||
}
|
||||
} else {
|
||||
return Ok(Some(vec![ComplexSelector {
|
||||
components: vec![ComplexSelectorComponent::Compound(CompoundSelector {
|
||||
return Ok(Some(vec![ComplexSelector::new(
|
||||
vec![ComplexSelectorComponent::Compound(CompoundSelector {
|
||||
components: resolved_members,
|
||||
})],
|
||||
line_break: false,
|
||||
}]));
|
||||
false,
|
||||
)]));
|
||||
}
|
||||
|
||||
let span = parent.span;
|
||||
@ -201,10 +201,7 @@ impl CompoundSelector {
|
||||
let mut components = complex.components;
|
||||
components.push(ComplexSelectorComponent::Compound(last));
|
||||
|
||||
Ok(ComplexSelector {
|
||||
components,
|
||||
line_break: complex.line_break,
|
||||
})
|
||||
Ok(ComplexSelector::new(components, complex.line_break))
|
||||
})
|
||||
.collect::<SassResult<Vec<ComplexSelector>>>()?,
|
||||
))
|
||||
|
@ -692,14 +692,8 @@ fn complex_is_parent_superselector(
|
||||
complex_one.push(ComplexSelectorComponent::Compound(base.clone()));
|
||||
complex_two.push(ComplexSelectorComponent::Compound(base));
|
||||
|
||||
ComplexSelector {
|
||||
components: complex_one,
|
||||
line_break: false,
|
||||
}
|
||||
.is_super_selector(&ComplexSelector {
|
||||
components: complex_two,
|
||||
line_break: false,
|
||||
})
|
||||
ComplexSelector::new(complex_one, false)
|
||||
.is_super_selector(&ComplexSelector::new(complex_two, false))
|
||||
}
|
||||
|
||||
/// Returns a list of all possible paths through the given lists.
|
||||
|
@ -10,8 +10,8 @@ use indexmap::IndexMap;
|
||||
use crate::error::SassResult;
|
||||
|
||||
use super::{
|
||||
ComplexSelector, ComplexSelectorComponent, CompoundSelector, Pseudo, SelectorList,
|
||||
SimpleSelector,
|
||||
ComplexSelector, ComplexSelectorComponent, ComplexSelectorHashSet, CompoundSelector, Pseudo,
|
||||
SelectorList, SimpleSelector,
|
||||
};
|
||||
|
||||
pub(crate) use extended_selector::ExtendedSelector;
|
||||
@ -99,7 +99,7 @@ pub(crate) struct Extender {
|
||||
/// exist to satisfy the [first law of extend][].
|
||||
///
|
||||
/// [first law of extend]: https://github.com/sass/sass/issues/324#issuecomment-4607184
|
||||
originals: HashSet<ComplexSelector>,
|
||||
originals: ComplexSelectorHashSet,
|
||||
|
||||
/// The mode that controls this extender's behavior.
|
||||
mode: ExtendMode,
|
||||
@ -129,7 +129,7 @@ impl Extender {
|
||||
extensions_by_extender: HashMap::new(),
|
||||
media_contexts: HashMap::new(),
|
||||
source_specificity: HashMap::new(),
|
||||
originals: HashSet::new(),
|
||||
originals: ComplexSelectorHashSet::new(),
|
||||
mode: ExtendMode::Normal,
|
||||
span,
|
||||
}
|
||||
@ -188,9 +188,7 @@ impl Extender {
|
||||
let mut extender = Extender::with_mode(mode, span);
|
||||
|
||||
if !selector.is_invisible() {
|
||||
extender
|
||||
.originals
|
||||
.extend(selector.components.iter().cloned());
|
||||
extender.originals.extend(selector.components.iter());
|
||||
}
|
||||
|
||||
Ok(extender.extend_list(selector, Some(&extensions), &None))
|
||||
@ -288,10 +286,7 @@ impl Extender {
|
||||
.into_iter()
|
||||
.take(i)
|
||||
.map(|component| {
|
||||
vec![ComplexSelector {
|
||||
components: vec![component],
|
||||
line_break: complex.line_break,
|
||||
}]
|
||||
vec![ComplexSelector::new(vec![component], complex.line_break)]
|
||||
})
|
||||
.collect(),
|
||||
);
|
||||
@ -302,19 +297,16 @@ impl Extender {
|
||||
}
|
||||
} else {
|
||||
match extended_not_expanded.as_mut() {
|
||||
Some(v) => v.push(vec![ComplexSelector {
|
||||
components: vec![ComplexSelectorComponent::Compound(component.clone())],
|
||||
line_break: false,
|
||||
}]),
|
||||
Some(v) => v.push(vec![ComplexSelector::new(
|
||||
vec![ComplexSelectorComponent::Compound(component.clone())],
|
||||
false,
|
||||
)]),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
} else if component.is_combinator() {
|
||||
match extended_not_expanded.as_mut() {
|
||||
Some(v) => v.push(vec![ComplexSelector {
|
||||
components: vec![component.clone()],
|
||||
line_break: false,
|
||||
}]),
|
||||
Some(v) => v.push(vec![ComplexSelector::new(vec![component.clone()], false)]),
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
@ -336,17 +328,17 @@ impl Extender {
|
||||
)
|
||||
.into_iter()
|
||||
.map(|components| {
|
||||
let output_complex = ComplexSelector {
|
||||
let output_complex = ComplexSelector::new(
|
||||
components,
|
||||
line_break: complex_has_line_break
|
||||
complex_has_line_break
|
||||
|| path.iter().any(|input_complex| input_complex.line_break),
|
||||
};
|
||||
);
|
||||
|
||||
// Make sure that copies of `complex` retain their status as "original"
|
||||
// selectors. This includes selectors that are modified because a :not()
|
||||
// was extended into.
|
||||
if first && self.originals.contains(&complex.clone()) {
|
||||
self.originals.insert(output_complex.clone());
|
||||
if first && self.originals.contains(&complex) {
|
||||
self.originals.insert(&output_complex);
|
||||
}
|
||||
first = false;
|
||||
|
||||
@ -517,10 +509,7 @@ impl Extender {
|
||||
Some(
|
||||
complexes
|
||||
.into_iter()
|
||||
.map(|components| ComplexSelector {
|
||||
components,
|
||||
line_break,
|
||||
})
|
||||
.map(|components| ComplexSelector::new(components, line_break))
|
||||
.collect::<Vec<ComplexSelector>>(),
|
||||
)
|
||||
});
|
||||
@ -742,12 +731,12 @@ impl Extender {
|
||||
fn extension_for_simple(&self, simple: SimpleSelector) -> Extension {
|
||||
let specificity = Some(*self.source_specificity.get(&simple).unwrap_or(&0_i32));
|
||||
Extension::one_off(
|
||||
ComplexSelector {
|
||||
components: vec![ComplexSelectorComponent::Compound(CompoundSelector {
|
||||
ComplexSelector::new(
|
||||
vec![ComplexSelectorComponent::Compound(CompoundSelector {
|
||||
components: vec![simple],
|
||||
})],
|
||||
line_break: false,
|
||||
},
|
||||
false,
|
||||
),
|
||||
specificity,
|
||||
true,
|
||||
self.span,
|
||||
@ -762,10 +751,7 @@ impl Extender {
|
||||
};
|
||||
let specificity = Some(self.source_specificity_for(&compound));
|
||||
Extension::one_off(
|
||||
ComplexSelector {
|
||||
components: vec![ComplexSelectorComponent::Compound(compound)],
|
||||
line_break: false,
|
||||
},
|
||||
ComplexSelector::new(vec![ComplexSelectorComponent::Compound(compound)], false),
|
||||
specificity,
|
||||
true,
|
||||
self.span,
|
||||
@ -883,7 +869,7 @@ impl Extender {
|
||||
) -> ExtendedSelector {
|
||||
if !selector.is_invisible() {
|
||||
for complex in selector.components.clone() {
|
||||
self.originals.insert(complex);
|
||||
self.originals.insert(&complex);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -128,10 +128,7 @@ impl SelectorList {
|
||||
unify_complex(vec![c1.components.clone(), c2.components]);
|
||||
if let Some(u) = unified {
|
||||
u.into_iter()
|
||||
.map(|c| ComplexSelector {
|
||||
components: c,
|
||||
line_break: false,
|
||||
})
|
||||
.map(|c| ComplexSelector::new(c, false))
|
||||
.collect()
|
||||
} else {
|
||||
Vec::new()
|
||||
@ -193,10 +190,10 @@ impl SelectorList {
|
||||
.map(move |parent_complex| {
|
||||
let mut components = parent_complex.components;
|
||||
components.append(&mut complex.components.clone());
|
||||
ComplexSelector {
|
||||
ComplexSelector::new(
|
||||
components,
|
||||
line_break: complex.line_break || parent_complex.line_break,
|
||||
}
|
||||
complex.line_break || parent_complex.line_break,
|
||||
)
|
||||
})
|
||||
.collect());
|
||||
}
|
||||
@ -245,10 +242,7 @@ impl SelectorList {
|
||||
.into_iter()
|
||||
.map(|new_complex| {
|
||||
i += 1;
|
||||
ComplexSelector {
|
||||
components: new_complex,
|
||||
line_break: line_breaks[i - 1],
|
||||
}
|
||||
ComplexSelector::new(new_complex, line_breaks[i - 1])
|
||||
})
|
||||
.collect())
|
||||
})
|
||||
|
@ -182,10 +182,7 @@ impl<'a, 'b> SelectorParser<'a, 'b> {
|
||||
return Err(("expected selector.", self.span).into());
|
||||
}
|
||||
|
||||
Ok(ComplexSelector {
|
||||
components,
|
||||
line_break,
|
||||
})
|
||||
Ok(ComplexSelector::new(components, line_break))
|
||||
}
|
||||
|
||||
fn parse_compound_selector(&mut self) -> SassResult<CompoundSelector> {
|
||||
|
@ -502,10 +502,7 @@ impl Pseudo {
|
||||
.any(move |complex1| {
|
||||
let mut components = parents.clone().unwrap_or_default();
|
||||
components.push(ComplexSelectorComponent::Compound(compound.clone()));
|
||||
complex1.is_super_selector(&ComplexSelector {
|
||||
components,
|
||||
line_break: false,
|
||||
})
|
||||
complex1.is_super_selector(&ComplexSelector::new(components, false))
|
||||
})
|
||||
}
|
||||
"has" | "host" | "host-context" => {
|
||||
|
@ -1906,6 +1906,22 @@ test!(
|
||||
}",
|
||||
"a, b {\n color: a;\n}\n"
|
||||
);
|
||||
test!(
|
||||
complex_selector_with_combinator_removed_by_complex_selector_without_combinator,
|
||||
"c b {
|
||||
@extend %d;
|
||||
}
|
||||
|
||||
c > b {
|
||||
@extend %d;
|
||||
}
|
||||
|
||||
%d {
|
||||
color: red;
|
||||
}",
|
||||
"c b {\n color: red;\n}\n"
|
||||
);
|
||||
|
||||
error!(
|
||||
extend_optional_keyword_not_complete,
|
||||
"a {
|
||||
|
Loading…
x
Reference in New Issue
Block a user