resolve @extend issues related to attrbitute equality

This commit is contained in:
ConnorSkees 2020-06-24 06:11:02 -04:00
parent 6fec0835f8
commit 4610a30024
3 changed files with 94 additions and 48 deletions

View File

@ -1,4 +1,7 @@
use std::fmt::{self, Display, Write};
use std::{
fmt::{self, Display, Write},
hash::{Hash, Hasher},
};
use codemap::Span;
@ -6,7 +9,7 @@ use crate::{common::QuoteKind, error::SassResult, parse::Parser, utils::is_ident
use super::{Namespace, QualifiedName};
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Clone, Debug)]
pub(crate) struct Attribute {
attr: QualifiedName,
value: String,
@ -15,6 +18,26 @@ pub(crate) struct Attribute {
span: Span,
}
impl PartialEq for Attribute {
fn eq(&self, other: &Self) -> bool {
self.attr == other.attr
&& self.value == other.value
&& self.modifier == other.modifier
&& self.op == other.op
}
}
impl Eq for Attribute {}
impl Hash for Attribute {
fn hash<H: Hasher>(&self, state: &mut H) {
self.attr.hash(state);
self.value.hash(state);
self.modifier.hash(state);
self.op.hash(state);
}
}
fn attribute_name(parser: &mut Parser<'_>, start: Span) -> SassResult<QualifiedName> {
let next = parser.toks.peek().ok_or(("Expected identifier.", start))?;
if next.kind == '*' {

View File

@ -206,25 +206,33 @@ impl Extender {
) -> SelectorList {
// This could be written more simply using Vec<Vec<T>>, but we want to avoid
// any allocations in the common case where no extends apply.
let mut extended: Vec<ComplexSelector> = Vec::new();
let mut extended: Option<Vec<ComplexSelector>> = None;
for i in 0..list.components.len() {
let complex = list.components.get(i).unwrap().clone();
if let Some(result) =
self.extend_complex(complex.clone(), extensions, media_query_context)
{
if extended.is_empty() && i != 0 {
extended = list.components[0..i].to_vec();
if extended.is_none() {
extended = Some(if i == 0 {
Vec::new()
} else {
list.components[0..i].to_vec()
});
}
extended.extend(result.into_iter());
} else if !extended.is_empty() {
match extended.as_mut() {
Some(v) => v.extend(result.into_iter()),
None => unreachable!(),
}
} else if let Some(extended) = extended.as_mut() {
extended.push(complex);
}
}
if extended.is_empty() {
return list;
}
let extended = match extended {
Some(v) => v,
None => return list,
};
SelectorList {
components: self.trim(extended, |complex| self.originals.contains(complex)),
@ -257,7 +265,7 @@ impl Extender {
//
// This could be written more simply using `Vec::into_iter::map`, but we want to avoid
// any allocations in the common case where no extends apply.
let mut extended_not_expanded: Vec<Vec<ComplexSelector>> = Vec::new();
let mut extended_not_expanded: Option<Vec<Vec<ComplexSelector>>> = None;
let complex_has_line_break = complex.line_break;
@ -266,8 +274,9 @@ impl Extender {
if let Some(extended) =
self.extend_compound(component, extensions, media_query_context)
{
if extended_not_expanded.is_empty() {
extended_not_expanded = complex
if extended_not_expanded.is_none() {
extended_not_expanded = Some(
complex
.components
.clone()
.into_iter()
@ -278,28 +287,36 @@ impl Extender {
line_break: complex.line_break,
}]
})
.collect();
.collect(),
);
}
match extended_not_expanded.as_mut() {
Some(v) => v.push(extended),
None => unreachable!(),
}
extended_not_expanded.push(extended);
} else {
extended_not_expanded.push(vec![ComplexSelector {
match extended_not_expanded.as_mut() {
Some(v) => v.push(vec![ComplexSelector {
components: vec![ComplexSelectorComponent::Compound(component.clone())],
line_break: false,
}])
}]),
None => {}
}
}
} else if let Some(component @ ComplexSelectorComponent::Combinator(..)) =
complex.components.get(i)
{
extended_not_expanded.push(vec![ComplexSelector {
match extended_not_expanded.as_mut() {
Some(v) => v.push(vec![ComplexSelector {
components: vec![component.clone()],
line_break: false,
}])
}]),
None => {}
}
}
}
if extended_not_expanded.is_empty() {
return None;
}
let extended_not_expanded = extended_not_expanded?;
let mut first = true;
@ -354,7 +371,7 @@ impl Extender {
// which targets are actually extended.
let mut targets_used: HashSet<SimpleSelector> = HashSet::new();
let mut options: Vec<Vec<Extension>> = Vec::new();
let mut options: Option<Vec<Vec<Extension>>> = None;
for i in 0..compound.components.len() {
let simple = compound.components.get(i).cloned().unwrap();
@ -365,21 +382,29 @@ impl Extender {
media_query_context,
&mut targets_used,
) {
if options.is_empty() && i != 0 {
options.push(vec![self.extension_for_compound(
if options.is_none() {
let mut new_options = Vec::new();
if i != 0 {
new_options.push(vec![self.extension_for_compound(
compound.components.clone().into_iter().take(i).collect(),
)]);
}
options.replace(new_options);
}
options.extend(extended.into_iter());
match options.as_mut() {
Some(v) => v.extend(extended.into_iter()),
None => unreachable!(),
}
} else {
options.push(vec![self.extension_for_simple(simple)]);
match options.as_mut() {
Some(v) => v.push(vec![self.extension_for_simple(simple)]),
None => {}
}
}
}
if options.is_empty() {
return None;
}
let options = options?;
// If `self.mode` isn't `ExtendMode::Normal` and we didn't use all the targets in
// `extensions`, extension fails for `compound`.

View File

@ -486,7 +486,6 @@ test!(
"-a [foo=bar].baz, -a [foo=bar][ns|foo=bar] {\n a: b;\n}\n"
);
test!(
#[ignore = "to investigate (too many selectors)"]
attribute_unification_5,
"%-a %-a [foo=bar].bar {a: b}
[foo=bar] {@extend .bar} -a {@extend %-a}
@ -644,7 +643,6 @@ test!(
"-a :not(.foo) {\n a: b;\n}\n"
);
test!(
#[ignore = "to investigate (too many selectors)"]
negation_unification_3,
"%-a :not([a=b]).baz {a: b}
:not([a = b]) {@extend .baz} -a {@extend %-a}