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

[Sass] Fix up a bunch of edge cases for the new @extend semantics.

This commit is contained in:
Nathan Weizenbaum 2010-05-07 22:25:37 -07:00
parent 84c1170f83
commit 8099b0e880
4 changed files with 132 additions and 38 deletions

View file

@ -199,22 +199,16 @@ module Haml
#
# @param x [Array]
# @param y [Array]
# @yield [a, b] An optional block to use in place of a check for equality
# between elements of `x` and `y`.
# @yieldreturn [Object, nil] If the two values register as equal,
# this will return the value to use in the LCS array.
# @return [Array] The LCS
def lcs(x, y)
def lcs(x, y, &block)
x = [nil, *x]
y = [nil, *y]
lcs_backtrace(lcs_table(x, y), x, y, x.size-1, y.size-1)
end
# Computes all single longest common subsequences for `x` and `y`.
#
# @param x [Array]
# @param y [Array]
# @return [Set<Array>] The LCSes
def lcs_all(x, y)
x = [nil, *x]
y = [nil, *y]
lcs_backtrace_all(lcs_table(x, y), x, y, x.size-1, y.size-1)
block ||= proc {|a, b| a == b && a}
lcs_backtrace(lcs_table(x, y, &block), x, y, x.size-1, y.size-1, &block)
end
# Returns information about the caller of the previous method.
@ -600,7 +594,7 @@ METHOD
(1...x.size).each do |i|
(1...y.size).each do |j|
c[i][j] =
if x[i] == y[j]
if yield x[i], y[j]
c[i-1][j-1] + 1
else
[c[i][j-1], c[i-1][j]].max
@ -612,22 +606,14 @@ METHOD
# Computes a single longest common subsequence for arrays x and y.
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_an_LCS)
def lcs_backtrace(c, x, y, i, j)
def lcs_backtrace(c, x, y, i, j, &block)
return [] if i == 0 || j == 0
return lcs_backtrace(c, x, y, i-1, j-1) << x[i] if x[i] == y[j]
return lcs_backtrace(c, x, y, i, j-1) if c[i][j-1] > c[i-1][j]
return lcs_backtrace(c, x, y, i-1, j)
end
if v = yield(x[i], y[j])
return lcs_backtrace(c, x, y, i-1, j-1, &block) << v
end
# Computes all longest common subsequences for arrays x and y.
# Algorithm from [Wikipedia](http://en.wikipedia.org/wiki/Longest_common_subsequence_problem#Reading_out_all_LCSs)
def lcs_backtrace_all(c, x, y, i, j)
return Set[[]] if i == 0 || j == 0
return lcs_backtrace_all(c, x, y, i-1, j-1).map {|z| z << x[i]}.to_set if x[i] == y[j]
r = Set.new
r.merge(lcs_backtrace_all(c, x, y, i, j-1)) if c[i][j-1] >= c[i-1][j]
r.merge(lcs_backtrace_all(c, x, y, i-1, j)) if c[i-1][j] >= c[i][j-1]
r
return lcs_backtrace(c, x, y, i, j-1, &block) if c[i][j-1] > c[i-1][j]
return lcs_backtrace(c, x, y, i-1, j, &block)
end
end
end

View file

@ -163,11 +163,16 @@ module Sass
seq1 = group_selectors(seq1)
seq2 = group_selectors(seq2)
lcs = Haml::Util.lcs(seq2, seq1)
lcs = Haml::Util.lcs(seq2, seq1) do |s1, s2|
next s1 if s1 == s2
next unless s1.first.is_a?(SimpleSequence) && s2.first.is_a?(SimpleSequence)
next s2 if subweave_superselector?(s1, s2)
next s1 if subweave_superselector?(s2, s1)
end
diff = []
until lcs.empty?
diff << chunks(seq1, seq2) {|s| s.first == lcs.first} << [lcs.shift]
diff << chunks(seq1, seq2) {|s| subweave_superselector?(s.first, lcs.first)} << [lcs.shift]
seq1.shift
seq2.shift
end
@ -201,6 +206,22 @@ module Sass
return newseq
end
def subweave_superselector?(sseq1, sseq2)
if sseq1.size > 1
# More complex selectors are never superselectors of less complex ones
return unless sseq2.size > 1
# .foo ~ .bar is a superselector of .foo + .bar
return unless sseq1[1] == "~" ? sseq2[1] != ">" : sseq2[1] == sseq1[1]
return sseq1.first.superselector?(sseq2.first) &&
subweave_superselector?(sseq1[2..-1], sseq2[2..-1])
elsif sseq2.size > 1
return true if sseq2[1] == ">" && sseq1.first.superselector?(sseq2.first)
return subweave_superselector?(sseq1, sseq2[2..-1])
else
sseq1.first.superselector?(sseq2.first)
end
end
def unify_heads(sseq1, sseq2)
return unless sseq2.size == 1 # Can't unify ".foo > .bar" and ".baz > .bang"
unified = sseq1.last.unify(sseq2.last.members) unless sseq1.last.is_a?(String) || sseq2.last.is_a?(String)

View file

@ -110,14 +110,11 @@ class UtilTest < Test::Unit::TestCase
assert_equal([1, 2], lcs([1, 2, 3, 4], [3, 4, 1, 2]))
end
def test_lcs_all
assert_equal(Set[[1, 2, 3]], lcs_all([1, 2, 3], [1, 2, 3]))
assert_equal(Set[[]], lcs_all([], [1, 2, 3]))
assert_equal(Set[[]], lcs_all([1, 2, 3], []))
assert_equal(Set[[1, 2, 3]], lcs_all([5, 1, 4, 2, 3, 17], [0, 0, 1, 2, 6, 3]))
assert_equal(Set[[1], [2], [3], [4]], lcs_all([1, 2, 3, 4], [4, 3, 2, 1]))
assert_equal(Set[[1, 2], [3, 4]], lcs_all([1, 2, 3, 4], [3, 4, 1, 2]))
def test_lcs_with_block
assert_equal(["1", "2", "3"],
lcs([1, 4, 2, 5, 3], [1, 2, 3]) {|a, b| a == b && a.to_s})
assert_equal([-4, 2, 8],
lcs([-5, 3, 2, 8], [-4, 1, 8]) {|a, b| (a - b).abs <= 1 && [a, b].max})
end
def test_silence_warnings

View file

@ -1024,6 +1024,26 @@ CSS
SCSS
end
def test_nested_extender_counts_extended_subselectors
assert_equal <<CSS, render(<<SCSS)
.a .bip.bop .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar {
a: b; }
CSS
.a .bip.bop .foo {a: b}
.b .bip .bar {@extend .foo}
SCSS
end
def test_nested_extender_counts_extended_superselectors
assert_equal <<CSS, render(<<SCSS)
.a .bip .foo, .a .b .bip.bop .bar, .b .a .bip.bop .bar {
a: b; }
CSS
.a .bip .foo {a: b}
.b .bip.bop .bar {@extend .foo}
SCSS
end
def test_nested_extender_with_child_selector
assert_equal <<CSS, render(<<SCSS)
.baz .foo, .baz foo > bar {
@ -1034,6 +1054,76 @@ foo > bar {@extend .foo}
SCSS
end
def test_nested_extender_finds_common_selectors_around_child_selector
assert_equal <<CSS, render(<<SCSS)
a > b c .c1, a > b c .c2 {
a: b; }
CSS
a > b c .c1 {a: b}
a c .c2 {@extend .c1}
SCSS
assert_equal <<CSS, render(<<SCSS)
a > b c .c1, a > b c .c2 {
a: b; }
CSS
a > b c .c1 {a: b}
b c .c2 {@extend .c1}
SCSS
end
def test_nested_extender_doesnt_find_common_selectors_around_adjacent_sibling_selector
assert_equal <<CSS, render(<<SCSS)
a + b c .c1, a + b a c .c2, a a + b c .c2 {
a: b; }
CSS
a + b c .c1 {a: b}
a c .c2 {@extend .c1}
SCSS
assert_equal <<CSS, render(<<SCSS)
a + b c .c1, a a + b c .c2 {
a: b; }
CSS
a + b c .c1 {a: b}
a b .c2 {@extend .c1}
SCSS
assert_equal <<CSS, render(<<SCSS)
a + b c .c1, a + b c .c2 {
a: b; }
CSS
a + b c .c1 {a: b}
b c .c2 {@extend .c1}
SCSS
end
def test_nested_extender_doesnt_find_common_selectors_around_sibling_selector
assert_equal <<CSS, render(<<SCSS)
a ~ b c .c1, a ~ b a c .c2, a a ~ b c .c2 {
a: b; }
CSS
a ~ b c .c1 {a: b}
a c .c2 {@extend .c1}
SCSS
assert_equal <<CSS, render(<<SCSS)
a ~ b c .c1, a a ~ b c .c2 {
a: b; }
CSS
a ~ b c .c1 {a: b}
a b .c2 {@extend .c1}
SCSS
assert_equal <<CSS, render(<<SCSS)
a ~ b c .c1, a ~ b c .c2 {
a: b; }
CSS
a ~ b c .c1 {a: b}
b c .c2 {@extend .c1}
SCSS
end
def test_nested_extender_with_early_child_selectors_doesnt_subseq_them
assert_equal <<CSS, render(<<SCSS)
.bip > .bap .foo, .bip > .bap .grip > .bap .bar, .grip > .bap .bip > .bap .bar {