resolve @extend
issues related to attrbitute equality
This commit is contained in:
parent
6fec0835f8
commit
4610a30024
@ -1,4 +1,7 @@
|
|||||||
use std::fmt::{self, Display, Write};
|
use std::{
|
||||||
|
fmt::{self, Display, Write},
|
||||||
|
hash::{Hash, Hasher},
|
||||||
|
};
|
||||||
|
|
||||||
use codemap::Span;
|
use codemap::Span;
|
||||||
|
|
||||||
@ -6,7 +9,7 @@ use crate::{common::QuoteKind, error::SassResult, parse::Parser, utils::is_ident
|
|||||||
|
|
||||||
use super::{Namespace, QualifiedName};
|
use super::{Namespace, QualifiedName};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
|
#[derive(Clone, Debug)]
|
||||||
pub(crate) struct Attribute {
|
pub(crate) struct Attribute {
|
||||||
attr: QualifiedName,
|
attr: QualifiedName,
|
||||||
value: String,
|
value: String,
|
||||||
@ -15,6 +18,26 @@ pub(crate) struct Attribute {
|
|||||||
span: Span,
|
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> {
|
fn attribute_name(parser: &mut Parser<'_>, start: Span) -> SassResult<QualifiedName> {
|
||||||
let next = parser.toks.peek().ok_or(("Expected identifier.", start))?;
|
let next = parser.toks.peek().ok_or(("Expected identifier.", start))?;
|
||||||
if next.kind == '*' {
|
if next.kind == '*' {
|
||||||
|
@ -206,25 +206,33 @@ impl Extender {
|
|||||||
) -> SelectorList {
|
) -> SelectorList {
|
||||||
// This could be written more simply using Vec<Vec<T>>, but we want to avoid
|
// 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.
|
// 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() {
|
for i in 0..list.components.len() {
|
||||||
let complex = list.components.get(i).unwrap().clone();
|
let complex = list.components.get(i).unwrap().clone();
|
||||||
|
|
||||||
if let Some(result) =
|
if let Some(result) =
|
||||||
self.extend_complex(complex.clone(), extensions, media_query_context)
|
self.extend_complex(complex.clone(), extensions, media_query_context)
|
||||||
{
|
{
|
||||||
if extended.is_empty() && i != 0 {
|
if extended.is_none() {
|
||||||
extended = list.components[0..i].to_vec();
|
extended = Some(if i == 0 {
|
||||||
|
Vec::new()
|
||||||
|
} else {
|
||||||
|
list.components[0..i].to_vec()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
extended.extend(result.into_iter());
|
match extended.as_mut() {
|
||||||
} else if !extended.is_empty() {
|
Some(v) => v.extend(result.into_iter()),
|
||||||
|
None => unreachable!(),
|
||||||
|
}
|
||||||
|
} else if let Some(extended) = extended.as_mut() {
|
||||||
extended.push(complex);
|
extended.push(complex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if extended.is_empty() {
|
let extended = match extended {
|
||||||
return list;
|
Some(v) => v,
|
||||||
}
|
None => return list,
|
||||||
|
};
|
||||||
|
|
||||||
SelectorList {
|
SelectorList {
|
||||||
components: self.trim(extended, |complex| self.originals.contains(complex)),
|
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
|
// 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.
|
// 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;
|
let complex_has_line_break = complex.line_break;
|
||||||
|
|
||||||
@ -266,8 +274,9 @@ impl Extender {
|
|||||||
if let Some(extended) =
|
if let Some(extended) =
|
||||||
self.extend_compound(component, extensions, media_query_context)
|
self.extend_compound(component, extensions, media_query_context)
|
||||||
{
|
{
|
||||||
if extended_not_expanded.is_empty() {
|
if extended_not_expanded.is_none() {
|
||||||
extended_not_expanded = complex
|
extended_not_expanded = Some(
|
||||||
|
complex
|
||||||
.components
|
.components
|
||||||
.clone()
|
.clone()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -278,28 +287,36 @@ impl Extender {
|
|||||||
line_break: complex.line_break,
|
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 {
|
} 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())],
|
components: vec![ComplexSelectorComponent::Compound(component.clone())],
|
||||||
line_break: false,
|
line_break: false,
|
||||||
}])
|
}]),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if let Some(component @ ComplexSelectorComponent::Combinator(..)) =
|
} else if let Some(component @ ComplexSelectorComponent::Combinator(..)) =
|
||||||
complex.components.get(i)
|
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()],
|
components: vec![component.clone()],
|
||||||
line_break: false,
|
line_break: false,
|
||||||
}])
|
}]),
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if extended_not_expanded.is_empty() {
|
let extended_not_expanded = extended_not_expanded?;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
|
||||||
@ -354,7 +371,7 @@ impl Extender {
|
|||||||
// which targets are actually extended.
|
// which targets are actually extended.
|
||||||
let mut targets_used: HashSet<SimpleSelector> = HashSet::new();
|
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() {
|
for i in 0..compound.components.len() {
|
||||||
let simple = compound.components.get(i).cloned().unwrap();
|
let simple = compound.components.get(i).cloned().unwrap();
|
||||||
@ -365,21 +382,29 @@ impl Extender {
|
|||||||
media_query_context,
|
media_query_context,
|
||||||
&mut targets_used,
|
&mut targets_used,
|
||||||
) {
|
) {
|
||||||
if options.is_empty() && i != 0 {
|
if options.is_none() {
|
||||||
options.push(vec![self.extension_for_compound(
|
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(),
|
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 {
|
} 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() {
|
let options = options?;
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If `self.mode` isn't `ExtendMode::Normal` and we didn't use all the targets in
|
// If `self.mode` isn't `ExtendMode::Normal` and we didn't use all the targets in
|
||||||
// `extensions`, extension fails for `compound`.
|
// `extensions`, extension fails for `compound`.
|
||||||
|
@ -486,7 +486,6 @@ test!(
|
|||||||
"-a [foo=bar].baz, -a [foo=bar][ns|foo=bar] {\n a: b;\n}\n"
|
"-a [foo=bar].baz, -a [foo=bar][ns|foo=bar] {\n a: b;\n}\n"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
#[ignore = "to investigate (too many selectors)"]
|
|
||||||
attribute_unification_5,
|
attribute_unification_5,
|
||||||
"%-a %-a [foo=bar].bar {a: b}
|
"%-a %-a [foo=bar].bar {a: b}
|
||||||
[foo=bar] {@extend .bar} -a {@extend %-a}
|
[foo=bar] {@extend .bar} -a {@extend %-a}
|
||||||
@ -644,7 +643,6 @@ test!(
|
|||||||
"-a :not(.foo) {\n a: b;\n}\n"
|
"-a :not(.foo) {\n a: b;\n}\n"
|
||||||
);
|
);
|
||||||
test!(
|
test!(
|
||||||
#[ignore = "to investigate (too many selectors)"]
|
|
||||||
negation_unification_3,
|
negation_unification_3,
|
||||||
"%-a :not([a=b]).baz {a: b}
|
"%-a :not([a=b]).baz {a: b}
|
||||||
:not([a = b]) {@extend .baz} -a {@extend %-a}
|
:not([a = b]) {@extend .baz} -a {@extend %-a}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user