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:
parent
ab244176a9
commit
1531519886
4 changed files with 83 additions and 36 deletions
|
@ -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**.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in a new issue