Skip to content

Commit

Permalink
Fix '&' multiplication
Browse files Browse the repository at this point in the history
Let'S do it by example:

  .ident_a, .ident_b {
    & + & + & { color: red; }
  }

We have to generate all permutations of the parent identifier list and
the amount of ampersand child identifiers. In the above case, the
exploded identifier list has ident_count**ampersand_count entries (8).

As a bonus, we get the same identifier sort order as lessc does.

Conflicts:
	lesscpy/plib/identifier.py
  • Loading branch information
saschpe committed Jan 24, 2014
1 parent 54b1e69 commit 277d8c3
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 183 deletions.
13 changes: 13 additions & 0 deletions lesscpy/lessc/utility.py
Expand Up @@ -11,6 +11,7 @@
from __future__ import print_function

import collections
import itertools
import math
import re
import sys
Expand Down Expand Up @@ -276,3 +277,15 @@ def convergent_round(value, ndigits=0):
nearest_even = integral_part + 0.5
return math.ceil(nearest_even)
return round(value, ndigits)


def permutations_with_replacement(iterable, r=None):
"""Return successive r length permutations of elements in the iterable.
Similar to itertools.permutation but withouth repeated values filtering.
"""
pool = tuple(iterable)
n = len(pool)
r = n if r is None else r
for indices in itertools.product(range(n), repeat=r):
yield list(pool[i] for i in indices)
66 changes: 38 additions & 28 deletions lesscpy/plib/identifier.py
Expand Up @@ -96,36 +96,46 @@ def root(self, scope, names):
if parent:
parent = parent[-1]
if parent.parsed:
return [self._pscn(part, n)
if part and part[0] not in self._subp
else n
for part in parent.parsed
for n in names]
parsed_names = []
for name in names:
ampersand_count = name.count('&')
if ampersand_count:
filtered_parts = []
for part in parent.parsed:
if part and part[0] not in self._subp:
filtered_parts.append(part)
permutations = list(utility.permutations_with_replacement(filtered_parts, ampersand_count))
for permutation in permutations:
parsed = []
for name_part in name:
if name_part == "&":
parent_part = permutation.pop(0)
if parsed and parsed[-1].endswith(']'):
parsed.extend(' ')
if parent_part[-1] == ' ':
parent_part.pop()
parsed.extend(parent_part)
else:
parsed.append(name_part)
parsed_names.append(parsed)
else:
# NOTE(saschpe): Maybe this code can be expressed with permutations too?
for part in parent.parsed:
if part and part[0] not in self._subp:
parsed = []
if name[0] == "@media":
parsed.extend(name)
else:
parsed.extend(part)
if part[-1] != ' ':
parsed.append(' ')
parsed.extend(name)
parsed_names.append(parsed)
else:
parsed_names.append(name)
return parsed_names
return names

def _pscn(self, parent, name):
"""
"""
parsed = []
if any((n for n in name if n == '&')):
for n in name:
if n == '&':
if parsed[-1].endswith(']'):
parsed.extend(' ')
if parent[-1] == ' ':
parent.pop()
parsed.extend(parent)
else:
parsed.append(n)
elif name[0] == "@media":
parsed.extend(name)
else:
parsed.extend(parent)
if parent[-1] != ' ':
parsed.append(' ')
parsed.extend(name)
return parsed

def raw(self, clean=False):
"""Raw identifier.
args:
Expand Down
10 changes: 5 additions & 5 deletions lesscpy/test/bootstrap3/css/theme.css
Expand Up @@ -9,16 +9,16 @@
box-shadow: inset 0 1px 0 rgba(255,255,255,.15), 0 1px 1px rgba(0,0,0,.075);
}
.btn-default:active,
.btn-default.active,
.btn-primary:active,
.btn-primary.active,
.btn-success:active,
.btn-success.active,
.btn-info:active,
.btn-info.active,
.btn-warning:active,
.btn-warning.active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
-webkit-box-shadow: inset 0 3px 5px rgba(0,0,0,.125);
box-shadow: inset 0 3px 5px rgba(0,0,0,.125);
Expand Down
2 changes: 1 addition & 1 deletion lesscpy/test/bootstrap3/css/theme.min.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 277d8c3

Please sign in to comment.