1
0
Fork 0
mirror of https://github.com/haml/haml.git synced 2022-11-09 12:33:31 -05:00

[Sass] Allow @extend loops.

This commit is contained in:
Nathan Weizenbaum 2010-04-28 18:48:29 -07:00
parent ab244176a9
commit 1531519886
4 changed files with 83 additions and 36 deletions

View file

@ -5,6 +5,28 @@
## 3.0.0.rc.3 (Unreleased)
### `@extend` Support
It's now possible to create a loop of `@extend` relations.
For example,
.blueLink {
color: blue;
@extend .link; }
.link {
font-weight: bold;
@extend .blueLink; }
Before, this would have raised an error.
Now it produces the following CSS:
.link, .blueLink {
color: blue; }
.blueLink, .link {
font-weight: bold; }
### Rails Beta Support
* **Support for Rails 3.0.0.beta1 has been dropped**.

View file

@ -76,10 +76,10 @@ module Sass
# by extending this selector with `extends`.
# These correspond to a {CommaSequence}'s {CommaSequence#members members array}.
# @see CommaSequence#do_extend
def do_extend(extends, supers = [])
def do_extend(extends, seen = Set.new)
paths = Haml::Util.paths(members.map do |sseq_or_op|
next [[sseq_or_op]] unless sseq_or_op.is_a?(SimpleSequence)
extended = sseq_or_op.do_extend(extends, supers)
extended = sseq_or_op.do_extend(extends, seen)
choices = extended.map {|seq| seq.members}
choices.unshift([sseq_or_op]) unless extended.any? {|seq| seq.superselector?(sseq_or_op)}
choices

View file

@ -61,7 +61,7 @@ module Sass
# @return [Array<Sequence>] A list of selectors generated
# by extending this selector with `extends`.
# @see CommaSequence#do_extend
def do_extend(extends, supers = [])
def do_extend(extends, seen = Set.new)
extends.get(members.to_set).map do |seq, sels|
# If A {@extend B} and C {...},
# seq is A, sels is B, and self is C
@ -69,13 +69,10 @@ module Sass
self_without_sel = self.members - sels
next unless unified = seq.members.last.unify(self_without_sel)
[sels, seq.members[0...-1] + [unified]]
end.compact.map {|sels, seq| [sels, Sequence.new(seq)]}.map do |sels, seq|
seqs = seq.do_extend(extends, supers.unshift(sels))
supers.shift
seqs
end.compact.map do |sels, seq|
seq = Sequence.new(seq)
seen.include?(sels) ? [] : seq.do_extend(extends, seen + [sels])
end.flatten.uniq
rescue SystemStackError
handle_extend_loop(supers)
end
# Unifies this selector with another {SimpleSequence}'s {SimpleSequence#members members array},
@ -140,33 +137,6 @@ module Sass
other.class == self.class && other.base.eql?(self.base) &&
Haml::Util.set_eql?(other.rest, self.rest)
end
private
# Raise a {Sass::SyntaxError} describing a loop of `@extend` directives.
#
# @param supers [Array<Simple>] The stack of selectors that contains the loop,
# ordered from deepest to most shallow.
# @raise [Sass::SyntaxError] Describing the loop
def handle_extend_loop(supers)
supers.inject([]) do |sseqs, sseq|
next sseqs.push(sseq) unless sseqs.first.eql?(sseq)
conses = Haml::Util.enum_cons(sseqs.push(sseq), 2).to_a
_, i = Haml::Util.enum_with_index(conses).max do |((_, sseq1), _), ((_, sseq2), _)|
sseq1.first.line <=> sseq2.first.line
end
loop = (conses[i..-1] + conses[0...i]).map do |sseq1, sseq2|
sel1 = SimpleSequence.new(sseq1).inspect
sel2 = SimpleSequence.new(sseq2).inspect
str = " #{sel1} extends #{sel2} on line #{sseq2.first.line}"
str << " of " << sseq2.first.filename if sseq2.first.filename
str
end.join(",\n")
raise Sass::SyntaxError.new("An @extend loop was found:\n#{loop}")
end
# Should never get here
raise Sass::SyntaxError.new("An @extend loop exists, but the exact loop couldn't be found")
end
end
end
end

View file

@ -1100,6 +1100,61 @@ CSS
SCSS
end
# Loops
def test_extend_self_loop
assert_equal <<CSS, render(<<SCSS)
.foo {
a: b; }
CSS
.foo {a: b; @extend .foo}
SCSS
end
def test_basic_extend_loop
assert_equal <<CSS, render(<<SCSS)
.bar, .foo {
a: b; }
.foo, .bar {
c: d; }
CSS
.foo {a: b; @extend .bar}
.bar {c: d; @extend .foo}
SCSS
end
def test_three_level_extend_loop
assert_equal <<CSS, render(<<SCSS)
.baz, .bar, .foo {
a: b; }
.foo, .baz, .bar {
c: d; }
.bar, .foo, .baz {
e: f; }
CSS
.foo {a: b; @extend .bar}
.bar {c: d; @extend .baz}
.baz {e: f; @extend .foo}
SCSS
end
def test_nested_extend_loop
assert_equal <<CSS, render(<<SCSS)
.bar, .bar .foo {
a: b; }
.bar .foo, .bar .foo .foo {
c: d; }
CSS
.bar {
a: b;
.foo {c: d; @extend .bar}
}
SCSS
end
def test_multiple_extender_merges_with_superset_selector
assert_equal <<CSS, render(<<SCSS)
a.bar.baz, a.foo {