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