summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEevee (Alex Munroe) <eevee.git@veekun.com>2013-09-11 17:01:22 -0700
committerEevee (Alex Munroe) <eevee.git@veekun.com>2013-09-11 17:01:22 -0700
commit296da4e357601b8531efec2d6ee342fa15d5c84a (patch)
treee07b76c88d5587eaea281967c4c20bb1bd3472b7
parent51075332edae1092ebb8fc8b98e567f1dd17e93b (diff)
downloadpyscss-296da4e357601b8531efec2d6ee342fa15d5c84a.tar.gz
Clean up @extend code in the compiler; fix multiple-selector case.
-rw-r--r--scss/__init__.py74
-rw-r--r--scss/rule.py35
2 files changed, 52 insertions, 57 deletions
diff --git a/scss/__init__.py b/scss/__init__.py
index 9407036..61e2f1f 100644
--- a/scss/__init__.py
+++ b/scss/__init__.py
@@ -50,6 +50,7 @@ from collections import defaultdict, deque
import glob
from itertools import product
import logging
+import operator
import os.path
import re
import sys
@@ -1266,7 +1267,7 @@ class Scss(object):
raw_selectors = calculator.apply_vars(block.prop)
c_selectors, c_parents = self.parse_selectors(raw_selectors)
- new_ancestry = rule.ancestry.with_selectors(c_selectors)
+ new_ancestry = rule.ancestry.with_nested_selectors(c_selectors)
_rule = SassRule(
source_file=rule.source_file,
@@ -1290,29 +1291,20 @@ class Scss(object):
For each part, create the inheritance parts from the @extends
"""
# Boy I wish I hadn't lost whatever work I'd done on this so far.
- # TODO: clean up variable names, method names (cross product?!), etc.
- # TODO: make Rules always contain Selectors, not strings.
- # TODO: fix the Selector rendering to put the right amount of space in
- # the right places
# TODO: child/sibling/etc selectors aren't handled correctly
- # TODO: preserve selector order
# TODO: %foo may not be handled correctly
# TODO: a whole bunch of unit tests for Selector parsing
# TODO: make sure this all works for kronuz
# TODO: steal a TONNNNNN of tests from ruby and sassc for this
- # TODO: can we skip all this woek if we've never seen an @extend?
# TODO: does this correctly handle extending a rule with a different
# ancestry?
-
-
# Game plan: for each rule that has an @extend, add its selectors to
# every rule that matches that @extend.
# First, rig a way to find arbitrary selectors quickly. Most selectors
# revolve around elements, classes, and IDs, so parse those out and use
# them as a rough key. Ignore order and duplication for now.
- from scss.selector import Selector
key_to_selectors = defaultdict(set)
selector_to_rules = defaultdict(list)
pos = 0
@@ -1329,41 +1321,45 @@ class Scss(object):
# rules.
for rule in self.rules:
for selector in rule.extends_selectors:
- extends_selectors = []
-
- import operator
- candidates = reduce(operator.and_, (key_to_selectors[key] for key in selector.lookup_key()))
- for candidate in candidates:
- if candidate.is_superset_of(selector):
- extends_selectors.append(candidate)
-
- if not extends_selectors:
- log.warn("no match found")
+ # This is a little dirty. intersection isn't a class method.
+ # Don't think about it too much.
+ candidates = set.intersection(*(
+ key_to_selectors[key] for key in selector.lookup_key()))
+ extendable_selectors = [
+ candidate for candidate in candidates
+ if candidate.is_superset_of(selector)]
+
+ if not extendable_selectors:
+ log.warn(
+ "Can't find any matching rules to extend: %s"
+ % selector.render())
continue
- # do magic here
- for extend_selector in extends_selectors:
- for parent_rule in selector_to_rules[extend_selector]:
- rule_selector, = rule.selectors # TODO
- new_parents = extend_selector.substitute(
- selector,
- rule_selector,
- )
-
- existing_parent_selectors = list(parent_rule.selectors)
- for parent in new_parents:
- existing_parent_selectors.append(parent)
- parent_rule.selectors = frozenset(existing_parent_selectors)
- parent_rule.dependent_rules.add(rule.position)
-
- # Update indices, in case any later rules try to extend
- # this one
- for parent in new_parents:
- key_to_selectors[parent].add(parent)
+ # Armed with a set of selectors that this rule can extend, do
+ # some substitution and modify the appropriate parent rules
+ for extendable_selector in extendable_selectors:
+ for parent_rule in selector_to_rules[extendable_selector]:
+ more_parent_selectors = []
+
+ for rule_selector in rule.selectors:
+ more_parent_selectors.extend(
+ extendable_selector.substitute(
+ selector, rule_selector))
+
+ for parent in more_parent_selectors:
+ # Update indices, in case any later rules try to
+ # extend this one
+ for key in parent.lookup_key():
+ key_to_selectors[key].add(parent)
# TODO this could lead to duplicates? maybe should
# be a set too
selector_to_rules[parent].append(parent_rule)
+ parent_rule.ancestry = (
+ parent_rule.ancestry.with_more_selectors(
+ more_parent_selectors))
+ parent_rule.dependent_rules.add(rule.position)
+
@print_timing(3)
def manage_order(self):
# order rules according with their dependencies
diff --git a/scss/rule.py b/scss/rule.py
index b263234..bd8b878 100644
--- a/scss/rule.py
+++ b/scss/rule.py
@@ -4,8 +4,6 @@ from __future__ import print_function
import six
import logging
-from scss.cssdefs import _has_placeholder_re
-from scss.selector import Selector
from scss.types import Value
@@ -193,28 +191,16 @@ class SassRule(object):
else:
return ()
- @selectors.setter
- def selectors(self, value):
- new_header = BlockSelectorHeader(value)
- if self.ancestry.headers and self.ancestry.headers[-1].is_selector:
- # Replace existing selectors
- new_headers = self.ancestry.headers[:-1] + (new_header,)
- else:
- # We're nested inside something; add new selectors
- new_headers = self.ancestry.headers + (new_header,)
-
- self.ancestry = RuleAncestry(new_headers)
-
@property
def file_and_line(self):
- """Returns the filename and line number where this rule originally
+ """Return the filename and line number where this rule originally
appears, in the form "foo.scss:3". Used for error messages.
"""
return "%s:%d" % (self.source_file.filename, self.lineno)
@property
def is_empty(self):
- """Returns whether this rule is considered "empty" -- i.e., has no
+ """Return whether this rule is considered "empty" -- i.e., has no
contents that should end up in the final CSS.
"""
if self.properties:
@@ -255,7 +241,7 @@ class RuleAncestry(object):
def __len__(self):
return len(self.headers)
- def with_selectors(self, c_selectors):
+ def with_nested_selectors(self, c_selectors):
if self.headers and self.headers[-1].is_selector:
# Need to merge with parent selectors
p_selectors = self.headers[-1].selectors
@@ -280,6 +266,19 @@ class RuleAncestry(object):
new_headers = self.headers + (BlockSelectorHeader(c_selectors),)
return RuleAncestry(new_headers)
+ def with_more_selectors(self, selectors):
+ """Return a new ancestry that also matches the given selectors. No
+ nesting is done.
+ """
+ if self.headers and self.headers[-1].is_selector:
+ new_selectors = self.headers[-1].selectors + tuple(selectors)
+ new_headers = self.headers[:-1] + (
+ BlockSelectorHeader(new_selectors),)
+ return RuleAncestry(new_headers)
+ else:
+ new_headers = self.headers + (BlockSelectorHeader(selectors),)
+ return RuleAncestry(new_headers)
+
class BlockHeader(object):
"""..."""
@@ -355,7 +354,7 @@ class BlockSelectorHeader(BlockHeader):
is_selector = True
def __init__(self, selectors):
- self.selectors = selectors
+ self.selectors = tuple(selectors)
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.selectors)