diff options
| author | Eevee (Alex Munroe) <eevee.git@veekun.com> | 2013-09-11 17:01:22 -0700 |
|---|---|---|
| committer | Eevee (Alex Munroe) <eevee.git@veekun.com> | 2013-09-11 17:01:22 -0700 |
| commit | 296da4e357601b8531efec2d6ee342fa15d5c84a (patch) | |
| tree | e07b76c88d5587eaea281967c4c20bb1bd3472b7 | |
| parent | 51075332edae1092ebb8fc8b98e567f1dd17e93b (diff) | |
| download | pyscss-296da4e357601b8531efec2d6ee342fa15d5c84a.tar.gz | |
Clean up @extend code in the compiler; fix multiple-selector case.
| -rw-r--r-- | scss/__init__.py | 74 | ||||
| -rw-r--r-- | scss/rule.py | 35 |
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) |
