wrap extended selectors in Rc<RefCell<T>>

This commit is contained in:
ConnorSkees 2020-06-23 02:36:30 -04:00
parent 0650f0ebcf
commit 7f8dc52e24
5 changed files with 71 additions and 57 deletions

View File

@ -84,7 +84,7 @@ impl Css {
if body.is_empty() { if body.is_empty() {
return Ok(Vec::new()); return Ok(Vec::new());
} }
let selector = selector.remove_placeholders(); let selector = selector.into_selector().remove_placeholders();
if selector.is_empty() { if selector.is_empty() {
return Ok(Vec::new()); return Ok(Vec::new());
} }

View File

@ -9,7 +9,9 @@ use crate::{
common::{Brackets, ListSeparator}, common::{Brackets, ListSeparator},
error::SassResult, error::SassResult,
scope::Scope, scope::Scope,
selector::{ComplexSelectorComponent, ExtendRule, Extender, Selector, SelectorParser}, selector::{
ComplexSelectorComponent, ExtendRule, ExtendedSelector, Extender, Selector, SelectorParser,
},
style::Style, style::Style,
unit::Unit, unit::Unit,
utils::{ utils::{
@ -40,7 +42,7 @@ pub(crate) enum Comment {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum Stmt { pub(crate) enum Stmt {
RuleSet { RuleSet {
selector: Selector, selector: ExtendedSelector,
body: Vec<Self>, body: Vec<Self>,
}, },
Style(Box<Style>), Style(Box<Style>),
@ -259,27 +261,25 @@ impl<'a> Parser<'a> {
SelectorOrStyle::Selector(init) => { SelectorOrStyle::Selector(init) => {
let at_root = self.at_root; let at_root = self.at_root;
self.at_root = false; self.at_root = false;
let selector = let selector = self
self.parse_selector(!self.super_selectors.is_empty(), false, init)?; .parse_selector(!self.super_selectors.is_empty(), false, init)?
let selector = Selector( .resolve_parent_selectors(
self.extender.add_selector( self.super_selectors.last(),
selector !at_root || self.at_root_has_selector,
.resolve_parent_selectors( )?;
self.super_selectors.last(),
!at_root || self.at_root_has_selector,
)?
.0,
None,
),
);
self.scopes.push(self.scopes.last().clone()); self.scopes.push(self.scopes.last().clone());
self.super_selectors.push(selector.clone()); self.super_selectors.push(selector.clone());
let extended_selector = self.extender.add_selector(selector.0, None);
let body = self.parse_stmt()?; let body = self.parse_stmt()?;
self.scopes.pop(); self.scopes.pop();
self.super_selectors.pop(); self.super_selectors.pop();
self.at_root = self.super_selectors.is_empty(); self.at_root = self.super_selectors.is_empty();
stmts.push(Stmt::RuleSet { selector, body }); stmts.push(Stmt::RuleSet {
selector: extended_selector,
body,
});
} }
}, },
} }
@ -1030,7 +1030,7 @@ impl<'a> Parser<'a> {
if !self.super_selectors.last().is_empty() { if !self.super_selectors.last().is_empty() {
body = vec![Stmt::RuleSet { body = vec![Stmt::RuleSet {
selector: self.super_selectors.last().clone(), selector: ExtendedSelector::new(self.super_selectors.last().clone().0),
body, body,
}]; }];
} }
@ -1082,7 +1082,7 @@ impl<'a> Parser<'a> {
if !self.super_selectors.last().is_empty() { if !self.super_selectors.last().is_empty() {
body = vec![Stmt::RuleSet { body = vec![Stmt::RuleSet {
selector: self.super_selectors.last().clone(), selector: ExtendedSelector::new(self.super_selectors.last().clone().0),
body, body,
}]; }];
} }
@ -1147,7 +1147,7 @@ impl<'a> Parser<'a> {
}) })
.collect::<SassResult<Vec<Stmt>>>()?; .collect::<SassResult<Vec<Stmt>>>()?;
let mut stmts = vec![Stmt::RuleSet { let mut stmts = vec![Stmt::RuleSet {
selector: at_rule_selector, selector: ExtendedSelector::new(at_rule_selector.0),
body: styles, body: styles,
}]; }];
stmts.extend(raw_stmts); stmts.extend(raw_stmts);
@ -1267,7 +1267,7 @@ impl<'a> Parser<'a> {
if !self.super_selectors.last().is_empty() { if !self.super_selectors.last().is_empty() {
body = vec![Stmt::RuleSet { body = vec![Stmt::RuleSet {
selector: self.super_selectors.last().clone(), selector: ExtendedSelector::new(self.super_selectors.last().clone().0),
body, body,
}]; }];
} }

View File

@ -0,0 +1,30 @@
use std::{
cell::RefCell,
hash::{Hash, Hasher},
rc::Rc,
};
use crate::selector::{Selector, SelectorList};
#[derive(Debug, Clone, Eq, PartialEq)]
pub(crate) struct ExtendedSelector(Rc<RefCell<SelectorList>>);
impl Hash for ExtendedSelector {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.borrow().hash(state);
}
}
impl ExtendedSelector {
pub fn new(selector: SelectorList) -> Self {
Self(Rc::new(RefCell::new(selector)))
}
pub fn into_selector(self) -> Selector {
Selector(self.0.borrow().clone())
}
pub fn set_inner(&mut self, selector: SelectorList) {
self.0.replace(selector);
}
}

View File

@ -14,12 +14,14 @@ use super::{
SimpleSelector, SimpleSelector,
}; };
pub(crate) use extended_selector::ExtendedSelector;
use extension::Extension; use extension::Extension;
pub(crate) use functions::unify_complex; pub(crate) use functions::unify_complex;
use functions::{paths, weave}; use functions::{paths, weave};
use merged::MergedExtension; use merged::MergedExtension;
pub(crate) use rule::ExtendRule; pub(crate) use rule::ExtendRule;
mod extended_selector;
mod extension; mod extension;
mod functions; mod functions;
mod merged; mod merged;
@ -62,7 +64,7 @@ pub(crate) struct Extender {
/// ///
/// This is used to find which selectors an `@extend` applies to and adjust /// This is used to find which selectors an `@extend` applies to and adjust
/// them. /// them.
selectors: HashMap<SimpleSelector, HashSet<SelectorList>>, selectors: HashMap<SimpleSelector, HashSet<ExtendedSelector>>,
/// A map from all extended simple selectors to the sources of those /// A map from all extended simple selectors to the sources of those
/// extensions. /// extensions.
@ -845,7 +847,7 @@ impl Extender {
mut selector: SelectorList, mut selector: SelectorList,
// span: Span, // span: Span,
media_query_context: Option<Vec<CssMediaQuery>>, media_query_context: Option<Vec<CssMediaQuery>>,
) -> SelectorList { ) -> ExtendedSelector {
// todo: we should be able to remove this variable and clone // todo: we should be able to remove this variable and clone
let original_selector = selector.clone(); let original_selector = selector.clone();
if !original_selector.is_invisible() { if !original_selector.is_invisible() {
@ -872,13 +874,14 @@ impl Extender {
.get_mut(&selector) .get_mut(&selector)
.replace(&mut media_query_context); .replace(&mut media_query_context);
} }
self.register_selector(selector.clone(), &selector); let extended_selector = ExtendedSelector::new(selector.clone());
selector self.register_selector(selector, extended_selector.clone());
extended_selector
} }
/// Registers the `SimpleSelector`s in `list` to point to `selector` in /// Registers the `SimpleSelector`s in `list` to point to `selector` in
/// `self.selectors`. /// `self.selectors`.
fn register_selector(&mut self, list: SelectorList, selector: &SelectorList) { fn register_selector(&mut self, list: SelectorList, selector: ExtendedSelector) {
for complex in list.components { for complex in list.components {
for component in complex.components { for component in complex.components {
if let ComplexSelectorComponent::Compound(component) = component { if let ComplexSelectorComponent::Compound(component) = component {
@ -893,7 +896,7 @@ impl Extender {
.. ..
}) = simple }) = simple
{ {
self.register_selector(simple_selector, selector); self.register_selector(simple_selector, selector.clone());
} }
} }
} }
@ -1115,16 +1118,16 @@ impl Extender {
/// Extend `extensions` using `new_extensions`. /// Extend `extensions` using `new_extensions`.
fn extend_existing_selectors( fn extend_existing_selectors(
&mut self, &mut self,
selectors: HashSet<SelectorList>, selectors: HashSet<ExtendedSelector>,
new_extensions: &HashMap<SimpleSelector, IndexMap<ComplexSelector, Extension>>, new_extensions: &HashMap<SimpleSelector, IndexMap<ComplexSelector, Extension>>,
) { ) {
for mut selector in selectors { for mut selector in selectors {
let old_value = selector.clone(); let old_value = selector.clone().into_selector().0;
selector = self.extend_list( selector.set_inner(self.extend_list(
old_value.clone(), old_value.clone(),
new_extensions, new_extensions,
&self.media_contexts.get(&selector).cloned(), &self.media_contexts.get(&old_value).cloned(),
); ));
/* /*
todo: error handling todo: error handling
} on SassException catch (error) { } on SassException catch (error) {
@ -1138,10 +1141,11 @@ impl Extender {
// If no extends actually happened (for example becaues unification // If no extends actually happened (for example becaues unification
// failed), we don't need to re-register the selector. // failed), we don't need to re-register the selector.
if old_value == selector { let selector_as_selector = selector.clone().into_selector().0;
if old_value == selector_as_selector {
continue; continue;
} }
self.register_selector(selector.clone(), &old_value); self.register_selector(selector_as_selector, selector);
} }
} }
} }

View File

@ -10,7 +10,6 @@ test!(
"a {\n color: red;\n}\n" "a {\n color: red;\n}\n"
); );
test!( test!(
#[ignore = "Rc<RefCell<Selector>>"]
list_extends_both_of_compound, list_extends_both_of_compound,
".foo.bar { ".foo.bar {
a: b a: b
@ -61,7 +60,6 @@ test!(
".foo, .baz {\n a: b;\n}\n\n.bar, .baz {\n c: d;\n}\n" ".foo, .baz {\n a: b;\n}\n\n.bar, .baz {\n c: d;\n}\n"
); );
test!( test!(
#[ignore = "to investigate (missing selectors)"]
class_extends_class_multiple_classes_extend_one, class_extends_class_multiple_classes_extend_one,
".foo {a: b} ".foo {a: b}
.bar {@extend .foo} .bar {@extend .foo}
@ -71,7 +69,6 @@ test!(
".foo, .bar, .bip, .baz {\n a: b;\n}\n" ".foo, .bar, .bip, .baz {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "different order"]
class_extends_class_all_parts_of_complex_selector_extended_by_one, class_extends_class_all_parts_of_complex_selector_extended_by_one,
".foo .bar {a: b} ".foo .bar {a: b}
.baz {@extend .foo; @extend .bar} .baz {@extend .foo; @extend .bar}
@ -86,7 +83,6 @@ test!(
".foo.bar, .baz {\n a: b;\n}\n" ".foo.bar, .baz {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "different order"]
class_extends_class_all_parts_of_complex_selector_extended_by_different, class_extends_class_all_parts_of_complex_selector_extended_by_different,
".foo .bar {a: b} ".foo .bar {a: b}
.baz {@extend .foo} .baz {@extend .foo}
@ -95,7 +91,6 @@ test!(
".foo .bar, .foo .bang, .baz .bar, .baz .bang {\n a: b;\n}\n" ".foo .bar, .foo .bang, .baz .bar, .baz .bang {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "different order"]
class_extends_class_all_parts_of_compound_selector_extended_by_different, class_extends_class_all_parts_of_compound_selector_extended_by_different,
".foo.bar {a: b} ".foo.bar {a: b}
.baz {@extend .foo} .baz {@extend .foo}
@ -104,7 +99,6 @@ test!(
".foo.bar, .foo.bang, .bar.baz, .baz.bang {\n a: b;\n}\n" ".foo.bar, .foo.bang, .bar.baz, .baz.bang {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "to investigate (missing selectors)"]
class_extends_class_simple_selector_extended_chain, class_extends_class_simple_selector_extended_chain,
".foo {a: b} ".foo {a: b}
.bar {@extend .foo} .bar {@extend .foo}
@ -612,10 +606,9 @@ test!(
".foo::bar, .baz:not(.bang)::bar {\n a: b;\n}\n" ".foo::bar, .baz:not(.bang)::bar {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "to investigate (parsing failure)"]
pseudoelement_goes_lefter_than_not_2, pseudoelement_goes_lefter_than_not_2,
"%a { "%a {
x:y; a:b;
} }
b:after:not(:first-child) { b:after:not(:first-child) {
@extend %a; @extend %a;
@ -667,7 +660,6 @@ test!(
".foo, .baz {\n a: b;\n}\n\n.bar, .baz {\n c: d;\n}\n" ".foo, .baz {\n a: b;\n}\n\n.bar, .baz {\n c: d;\n}\n"
); );
test!( test!(
#[ignore = "different order"]
redundant_selector_elimination, redundant_selector_elimination,
".foo.bar {a: b} ".foo.bar {a: b}
.x {@extend .foo, .bar} .x {@extend .foo, .bar}
@ -1353,7 +1345,6 @@ test!(
"#context .bat, .bar .baz {\n a: b;\n}\n" "#context .bat, .bar .baz {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "different order"]
placeholder_with_multiple_extenders, placeholder_with_multiple_extenders,
"%foo {a: b} "%foo {a: b}
.bar {@extend %foo} .bar {@extend %foo}
@ -1454,7 +1445,6 @@ test!(
"a.bar {\n a: b;\n}\n" "a.bar {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "@extend chains do not yet work"]
psuedo_element_superselector_1, psuedo_element_superselector_1,
"%x#bar {a: b} // Add an id to make the results have high specificity "%x#bar {a: b} // Add an id to make the results have high specificity
%y, %y::fblthp {@extend %x} %y, %y::fblthp {@extend %x}
@ -1463,7 +1453,6 @@ test!(
"a#bar, a#bar::fblthp {\n a: b;\n}\n" "a#bar, a#bar::fblthp {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "@extend chains do not yet work"]
psuedo_element_superselector_2, psuedo_element_superselector_2,
"%x#bar {a: b} "%x#bar {a: b}
%y, %y:fblthp {@extend %x} %y, %y:fblthp {@extend %x}
@ -1472,7 +1461,6 @@ test!(
"a#bar {\n a: b;\n}\n" "a#bar {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "@extend chains do not yet work"]
psuedo_element_superselector_3, psuedo_element_superselector_3,
"%x#bar {a: b} "%x#bar {a: b}
%y, %y:first-line {@extend %x} %y, %y:first-line {@extend %x}
@ -1481,7 +1469,6 @@ test!(
"a#bar, a#bar:first-line {\n a: b;\n}\n" "a#bar, a#bar:first-line {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "@extend chains do not yet work"]
psuedo_element_superselector_4, psuedo_element_superselector_4,
"%x#bar {a: b} "%x#bar {a: b}
%y, %y:first-letter {@extend %x} %y, %y:first-letter {@extend %x}
@ -1490,7 +1477,6 @@ test!(
"a#bar, a#bar:first-letter {\n a: b;\n}\n" "a#bar, a#bar:first-letter {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "@extend chains do not yet work"]
psuedo_element_superselector_5, psuedo_element_superselector_5,
"%x#bar {a: b} "%x#bar {a: b}
%y, %y:before {@extend %x} %y, %y:before {@extend %x}
@ -1499,7 +1485,6 @@ test!(
"a#bar, a#bar:before {\n a: b;\n}\n" "a#bar, a#bar:before {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "@extend chains do not yet work"]
psuedo_element_superselector_6, psuedo_element_superselector_6,
"%x#bar {a: b} "%x#bar {a: b}
%y, %y:after {@extend %x} %y, %y:after {@extend %x}
@ -1508,7 +1493,6 @@ test!(
"a#bar, a#bar:after {\n a: b;\n}\n" "a#bar, a#bar:after {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "super selectors shouldn't be resolved lazily"]
multiple_source_redundancy_elimination, multiple_source_redundancy_elimination,
"%default-color {color: red} "%default-color {color: red}
%alt-color {color: green} %alt-color {color: green}
@ -1555,7 +1539,6 @@ test!(
".parent1 .parent2 .child1.child2, .parent2 .parent1 .child1.child2 {\n a: b;\n}\n" ".parent1 .parent2 .child1.child2, .parent2 .parent1 .child1.child2 {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "to investigate (parsing failure)"]
nested_extend_specificity, nested_extend_specificity,
"%foo {a: b} "%foo {a: b}
@ -1661,7 +1644,6 @@ test!(
"> .foo, > flip,\n> foo bar {\n a: b;\n}\n" "> .foo, > flip,\n> foo bar {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "to investigate (missing selectors)"]
extended_parent_and_child_redundancy_elimination, extended_parent_and_child_redundancy_elimination,
"a { "a {
b {a: b} b {a: b}
@ -1693,7 +1675,6 @@ test!(
"a.foo, .foo {\n a: b;\n}\n" "a.foo, .foo {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "Rc<RefCell<Selector>>"]
cross_branch_redundancy_elimination_1, cross_branch_redundancy_elimination_1,
"%x .c %y {a: b} "%x .c %y {a: b}
.a, .b {@extend %x} .a, .b {@extend %x}
@ -1702,7 +1683,6 @@ test!(
".a .c .d, .b .c .a .d {\n a: b;\n}\n" ".a .c .d, .b .c .a .d {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "to investigate (missing selectors)"]
cross_branch_redundancy_elimination_2, cross_branch_redundancy_elimination_2,
".e %z {a: b} ".e %z {a: b}
%x .c %y {@extend %z} %x .c %y {@extend %z}
@ -1781,6 +1761,7 @@ test!(
"@media screen {\n a {\n x: y;\n }\n\n @page {}\n}\n" "@media screen {\n a {\n x: y;\n }\n\n @page {}\n}\n"
); );
test!( test!(
#[ignore = "to investigate"]
escaped_selector, escaped_selector,
"// Escapes in selectors' identifiers should be normalized before `@extend` is "// Escapes in selectors' identifiers should be normalized before `@extend` is
// applied. // applied.
@ -1853,7 +1834,6 @@ test!(
":not(:not(.x)) {\n a: b;\n}\n" ":not(:not(.x)) {\n a: b;\n}\n"
); );
test!( test!(
#[ignore = "different order"]
selector_list, selector_list,
".foo {a: b} ".foo {a: b}
.bar {x: y} .bar {x: y}